Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Side by Side Diff: gclient.py

Issue 1730014: Revert r45652 and r45653. It broke the single file export... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools/
Patch Set: Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | gclient_utils.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 """A wrapper script to manage a set of client modules in different SCM. 6 """A wrapper script to manage a set of client modules in different SCM.
7 7
8 This script is intended to be used to help basic management of client 8 This script is intended to be used to help basic management of client
9 program sources residing in one or more Subversion modules and Git 9 program sources residing in one or more Subversion modules and Git
10 repositories, along with other modules it depends on, also in Subversion or Git, 10 repositories, along with other modules it depends on, also in Subversion or Git,
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
51 Example: 51 Example:
52 hooks = [ 52 hooks = [
53 { "pattern": "\\.(gif|jpe?g|pr0n|png)$", 53 { "pattern": "\\.(gif|jpe?g|pr0n|png)$",
54 "action": ["python", "image_indexer.py", "--all"]}, 54 "action": ["python", "image_indexer.py", "--all"]},
55 ] 55 ]
56 """ 56 """
57 57
58 __author__ = "darinf@gmail.com (Darin Fisher)" 58 __author__ = "darinf@gmail.com (Darin Fisher)"
59 __version__ = "0.3.4" 59 __version__ = "0.3.4"
60 60
61 import copy
62 import errno 61 import errno
63 import logging 62 import logging
64 import optparse 63 import optparse
65 import os 64 import os
66 import pprint 65 import pprint
67 import re 66 import re
68 import sys 67 import sys
69 import urlparse 68 import urlparse
70 import urllib 69 import urllib
71 70
(...skipping 620 matching lines...) Expand 10 before | Expand all | Expand 10 after
692 return 691 return
693 692
694 # Run hooks on the basis of whether the files from the gclient operation 693 # Run hooks on the basis of whether the files from the gclient operation
695 # match each hook's pattern. 694 # match each hook's pattern.
696 for hook_dict in hooks: 695 for hook_dict in hooks:
697 pattern = re.compile(hook_dict['pattern']) 696 pattern = re.compile(hook_dict['pattern'])
698 matching_file_list = [f for f in file_list if pattern.search(f)] 697 matching_file_list = [f for f in file_list if pattern.search(f)]
699 if matching_file_list: 698 if matching_file_list:
700 self._RunHookAction(hook_dict, matching_file_list) 699 self._RunHookAction(hook_dict, matching_file_list)
701 700
702 def GetSCMCommandClosure(self, path, url, revision, command, args, file_list):
703 """Gets a closure that runs a SCM command on a particular dependency."""
704 def _Closure():
705 logging.debug("Running %s in %s to %s %s" % (command, path, url,
706 revision))
707 options = copy.copy(self._options)
708 options.revision = revision
709 scm = gclient_scm.CreateSCM(url, self._root_dir, path)
710 scm.RunCommand(command, options, args, file_list)
711 return _Closure
712
713 def RunOnDeps(self, command, args): 701 def RunOnDeps(self, command, args):
714 """Runs a command on each dependency in a client and its dependencies. 702 """Runs a command on each dependency in a client and its dependencies.
715 703
716 The module's dependencies are specified in its top-level DEPS files. 704 The module's dependencies are specified in its top-level DEPS files.
717 705
718 Args: 706 Args:
719 command: The command to use (e.g., 'status' or 'diff') 707 command: The command to use (e.g., 'status' or 'diff')
720 args: list of str - extra arguments to add to the command line. 708 args: list of str - extra arguments to add to the command line.
721 709
722 Raises: 710 Raises:
(...skipping 20 matching lines...) Expand all
743 if not solutions: 731 if not solutions:
744 raise gclient_utils.Error("No solution specified") 732 raise gclient_utils.Error("No solution specified")
745 733
746 # When running runhooks --force, there's no need to consult the SCM. 734 # When running runhooks --force, there's no need to consult the SCM.
747 # All known hooks are expected to run unconditionally regardless of working 735 # All known hooks are expected to run unconditionally regardless of working
748 # copy state, so skip the SCM status check. 736 # copy state, so skip the SCM status check.
749 run_scm = not (command == 'runhooks' and self._options.force) 737 run_scm = not (command == 'runhooks' and self._options.force)
750 738
751 entries = {} 739 entries = {}
752 entries_deps_content = {} 740 entries_deps_content = {}
741 file_list = []
742 # Run on the base solutions first.
743 for solution in solutions:
744 name = solution["name"]
745 deps_file = solution.get("deps_file", self._options.deps_file)
746 if '/' in deps_file or '\\' in deps_file:
747 raise gclient_utils.Error('deps_file name must not be a path, just a '
748 'filename.')
749 if name in entries:
750 raise gclient_utils.Error("solution %s specified more than once" % name)
751 url = solution["url"]
752 entries[name] = url
753 if run_scm and url:
754 self._options.revision = revision_overrides.get(name)
755 scm = gclient_scm.CreateSCM(url, self._root_dir, name)
756 scm.RunCommand(command, self._options, args, file_list)
757 file_list = [os.path.join(name, f.strip()) for f in file_list]
758 self._options.revision = None
759 try:
760 deps_content = gclient_utils.FileRead(
761 os.path.join(self._root_dir, name, deps_file))
762 except IOError, e:
763 if e.errno != errno.ENOENT:
764 raise
765 deps_content = ""
766 entries_deps_content[name] = deps_content
753 767
754 # To avoid threading issues, all file lists get constructed separately then 768 # Process the dependencies next (sort alphanumerically to ensure that
755 # gathered in a flattened list at the end. 769 # containing directories get populated first and for readability)
756 file_list_list = [] 770 deps = self._ParseAllDeps(entries, entries_deps_content)
757 file_list_dict = {} 771 deps_to_process = deps.keys()
772 deps_to_process.sort()
758 773
759 thread_pool = gclient_utils.ThreadPool(self._options.jobs) 774 # First pass for direct dependencies.
760 thread_pool.Start() 775 if command == 'update' and not self._options.verbose:
776 pm = Progress('Syncing projects', len(deps_to_process))
777 for d in deps_to_process:
778 if command == 'update' and not self._options.verbose:
779 pm.update()
780 if type(deps[d]) == str:
781 url = deps[d]
782 entries[d] = url
783 if run_scm:
784 self._options.revision = revision_overrides.get(d)
785 scm = gclient_scm.CreateSCM(url, self._root_dir, d)
786 scm.RunCommand(command, self._options, args, file_list)
787 self._options.revision = None
788 elif isinstance(deps[d], self.FileImpl):
789 file = deps[d]
790 self._options.revision = file.GetRevision()
791 if run_scm:
792 scm = gclient_scm.CreateSCM(file.GetPath(), self._root_dir, d)
793 scm.RunCommand("updatesingle", self._options,
794 args + [file.GetFilename()], file_list)
795
796 if command == 'update' and not self._options.verbose:
797 pm.end()
761 798
762 try: 799 # Second pass for inherited deps (via the From keyword)
763 # Run on the base solutions first. 800 for d in deps_to_process:
764 for solution in solutions: 801 if isinstance(deps[d], self.FromImpl):
765 name = solution["name"] 802 filename = os.path.join(self._root_dir,
766 deps_file = solution.get("deps_file", self._options.deps_file) 803 deps[d].module_name,
767 if '/' in deps_file or '\\' in deps_file: 804 self._options.deps_file)
768 raise gclient_utils.Error('deps_file name must not be a path, just a ' 805 content = gclient_utils.FileRead(filename)
769 'filename.') 806 sub_deps = self._ParseSolutionDeps(deps[d].module_name, content, {},
770 if name in entries: 807 False)
771 raise gclient_utils.Error( 808 # Getting the URL from the sub_deps file can involve having to resolve
772 "solution %s specified more than once" % name) 809 # a File() or having to resolve a relative URL. To resolve relative
773 url = solution["url"] 810 # URLs, we need to pass in the orignal sub deps URL.
774 entries[name] = url 811 sub_deps_base_url = deps[deps[d].module_name]
775 if run_scm and url: 812 url = deps[d].GetUrl(d, sub_deps_base_url, self._root_dir, sub_deps)
776 revision = revision_overrides.get(name) 813 entries[d] = url
777 file_list = [] 814 if run_scm:
778 file_list_dict[name] = file_list 815 self._options.revision = revision_overrides.get(d)
779 thread_pool.AddJob(self.GetSCMCommandClosure( 816 scm = gclient_scm.CreateSCM(url, self._root_dir, d)
780 name, url, revision, command, args, file_list)) 817 scm.RunCommand(command, self._options, args, file_list)
781 818 self._options.revision = None
782 thread_pool.WaitJobs()
783
784 for solution in solutions:
785 name = solution["name"]
786 deps_file = solution.get("deps_file", self._options.deps_file)
787 try:
788 deps_content = gclient_utils.FileRead(
789 os.path.join(self._root_dir, name, deps_file))
790 except IOError, e:
791 if e.errno != errno.ENOENT:
792 raise
793 deps_content = ""
794 entries_deps_content[name] = deps_content
795 try:
796 file_list_list.append([os.path.join(name, f.strip())
797 for f in file_list_dict[name]])
798 except KeyError:
799 # We may not have added the file list to the dict, see tests above.
800 # Instead of duplicating the tests, it's less fragile to just ignore
801 # the exception.
802 pass
803
804 # Process the dependencies next (sort alphanumerically to ensure that
805 # containing directories get populated first and for readability)
806 # TODO(piman): when using multiple threads, the ordering is not ensured.
807 # In many cases (e.g. updates to an existing checkout where DEPS don't
808 # move between directories), it'll still be correct but for completeness
809 # this should be fixed.
810 deps = self._ParseAllDeps(entries, entries_deps_content)
811 deps_to_process = deps.keys()
812 deps_to_process.sort()
813
814 # First pass for direct dependencies.
815 if command == 'update' and not self._options.verbose:
816 pm = Progress('Syncing projects', len(deps_to_process))
817
818 for d in deps_to_process:
819 if command == 'update' and not self._options.verbose:
820 pm.update()
821 file_list = []
822 file_list_list.append(file_list)
823 if type(deps[d]) == str:
824 url = deps[d]
825 entries[d] = url
826 if run_scm:
827 revision = revision_overrides.get(d)
828 thread_pool.AddJob(self.GetSCMCommandClosure(d, url, revision,
829 command, args,
830 file_list))
831 elif isinstance(deps[d], self.FileImpl):
832 file = deps[d]
833 if run_scm:
834 revision = file.GetRevision()
835 thread_pool.AddJob(self.GetSCMCommandClosure(
836 d, url, revision, "updatesingle", args + [file.GetFilename()],
837 file_list))
838
839 thread_pool.WaitJobs()
840
841 if command == 'update' and not self._options.verbose:
842 pm.end()
843
844 # Second pass for inherited deps (via the From keyword)
845 for d in deps_to_process:
846 if isinstance(deps[d], self.FromImpl):
847 filename = os.path.join(self._root_dir,
848 deps[d].module_name,
849 self._options.deps_file)
850 content = gclient_utils.FileRead(filename)
851 sub_deps = self._ParseSolutionDeps(deps[d].module_name, content, {},
852 False)
853 # Getting the URL from the sub_deps file can involve having to resolve
854 # a File() or having to resolve a relative URL. To resolve relative
855 # URLs, we need to pass in the orignal sub deps URL.
856 sub_deps_base_url = deps[deps[d].module_name]
857 url = deps[d].GetUrl(d, sub_deps_base_url, self._root_dir, sub_deps)
858 entries[d] = url
859 if run_scm:
860 revision = revision_overrides.get(d)
861 file_list = []
862 file_list_list.append(file_list)
863 thread_pool.AddJob(self.GetSCMCommandClosure(d, url, revision,
864 command, args,
865 file_list))
866
867 thread_pool.WaitJobs()
868 finally:
869 thread_pool.Stop()
870
871 file_list = sum(file_list_list, [])
872 819
873 # Convert all absolute paths to relative. 820 # Convert all absolute paths to relative.
874 for i in range(len(file_list)): 821 for i in range(len(file_list)):
875 # TODO(phajdan.jr): We should know exactly when the paths are absolute. 822 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
876 # It depends on the command being executed (like runhooks vs sync). 823 # It depends on the command being executed (like runhooks vs sync).
877 if not os.path.isabs(file_list[i]): 824 if not os.path.isabs(file_list[i]):
878 continue 825 continue
879 826
880 prefix = os.path.commonprefix([self._root_dir.lower(), 827 prefix = os.path.commonprefix([self._root_dir.lower(),
881 file_list[i].lower()]) 828 file_list[i].lower()])
(...skipping 448 matching lines...) Expand 10 before | Expand all | Expand 10 after
1330 help=("on update, delete any unexpected " 1277 help=("on update, delete any unexpected "
1331 "unversioned trees that are in the checkout")) 1278 "unversioned trees that are in the checkout"))
1332 option_parser.add_option("", "--snapshot", action="store_true", default=False, 1279 option_parser.add_option("", "--snapshot", action="store_true", default=False,
1333 help=("(revinfo only), create a snapshot file " 1280 help=("(revinfo only), create a snapshot file "
1334 "of the current version of all repositories")) 1281 "of the current version of all repositories"))
1335 option_parser.add_option("", "--name", 1282 option_parser.add_option("", "--name",
1336 help="specify alternate relative solution path") 1283 help="specify alternate relative solution path")
1337 option_parser.add_option("", "--gclientfile", default=None, 1284 option_parser.add_option("", "--gclientfile", default=None,
1338 metavar="FILENAME", 1285 metavar="FILENAME",
1339 help=("specify an alternate .gclient file")) 1286 help=("specify an alternate .gclient file"))
1340 option_parser.add_option("-j", "--jobs", default=1, type="int",
1341 help=("specify how many SCM commands can run in "
1342 "parallel"))
1343 1287
1344 if len(argv) < 2: 1288 if len(argv) < 2:
1345 # Users don't need to be told to use the 'help' command. 1289 # Users don't need to be told to use the 'help' command.
1346 option_parser.print_help() 1290 option_parser.print_help()
1347 return 1 1291 return 1
1348 # Add manual support for --version as first argument. 1292 # Add manual support for --version as first argument.
1349 if argv[1] == '--version': 1293 if argv[1] == '--version':
1350 option_parser.print_version() 1294 option_parser.print_version()
1351 return 0 1295 return 0
1352 1296
(...skipping 24 matching lines...) Expand all
1377 1321
1378 if "__main__" == __name__: 1322 if "__main__" == __name__:
1379 try: 1323 try:
1380 result = Main(sys.argv) 1324 result = Main(sys.argv)
1381 except gclient_utils.Error, e: 1325 except gclient_utils.Error, e:
1382 print >> sys.stderr, "Error: %s" % str(e) 1326 print >> sys.stderr, "Error: %s" % str(e)
1383 result = 1 1327 result = 1
1384 sys.exit(result) 1328 sys.exit(result)
1385 1329
1386 # vim: ts=2:sw=2:tw=80:et: 1330 # vim: ts=2:sw=2:tw=80:et:
OLDNEW
« no previous file with comments | « no previous file | gclient_utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698