OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 dependency manager for Git.""" |
7 # Files | 7 # Files |
8 # .gclient : Current client configuration, written by 'config' command. | 8 # .gclient : Current client configuration, written by 'config' command. |
9 # Format is a Python script defining 'solutions', a list whose | 9 # Format is a Python script defining 'solutions', a list whose |
10 # entries each are maps binding the strings "name" and "url" | 10 # entries each are maps binding the strings "name" and "url" |
11 # to strings specifying the name and location of the client | 11 # to strings specifying the name and location of the client |
12 # module, as well as "custom_deps" to a map similar to the | 12 # module, as well as "custom_deps" to a map similar to the |
13 # deps section of the DEPS file below, as well as | 13 # deps section of the DEPS file below, as well as |
14 # "custom_hooks" to a list similar to the hooks sections of | 14 # "custom_hooks" to a list similar to the hooks sections of |
15 # the DEPS file below. | 15 # the DEPS file below. |
16 # .gclient_entries : A cache constructed by 'update' command. Format is a | 16 # .gclient_entries : A cache constructed by 'update' command. Format is a |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
166 | 166 |
167 sub_target_name is an optional parameter if the module name in the other | 167 sub_target_name is an optional parameter if the module name in the other |
168 DEPS file is different. E.g., you might want to map src/net to net.""" | 168 DEPS file is different. E.g., you might want to map src/net to net.""" |
169 self.module_name = module_name | 169 self.module_name = module_name |
170 self.sub_target_name = sub_target_name | 170 self.sub_target_name = sub_target_name |
171 | 171 |
172 def __str__(self): | 172 def __str__(self): |
173 return 'From(%s, %s)' % (repr(self.module_name), | 173 return 'From(%s, %s)' % (repr(self.module_name), |
174 repr(self.sub_target_name)) | 174 repr(self.sub_target_name)) |
175 | 175 |
176 class FileImpl(object): | |
177 """Used to implement the File('') syntax which lets you sync a single file | |
178 from a SVN repo.""" | |
179 | |
180 def __init__(self, file_location): | |
181 self.file_location = file_location | |
182 | |
183 def __str__(self): | |
184 return 'File("%s")' % self.file_location | |
185 | |
186 def GetPath(self): | |
187 return os.path.split(self.file_location)[0] | |
188 | |
189 def GetFilename(self): | |
190 rev_tokens = self.file_location.split('@') | |
191 return os.path.split(rev_tokens[0])[1] | |
192 | |
193 def GetRevision(self): | |
194 rev_tokens = self.file_location.split('@') | |
195 if len(rev_tokens) > 1: | |
196 return rev_tokens[1] | |
197 return None | |
198 | |
199 class VarImpl(object): | 176 class VarImpl(object): |
200 def __init__(self, custom_vars, local_scope): | 177 def __init__(self, custom_vars, local_scope): |
201 self._custom_vars = custom_vars | 178 self._custom_vars = custom_vars |
202 self._local_scope = local_scope | 179 self._local_scope = local_scope |
203 | 180 |
204 def Lookup(self, var_name): | 181 def Lookup(self, var_name): |
205 """Implements the Var syntax.""" | 182 """Implements the Var syntax.""" |
206 if var_name in self._custom_vars: | 183 if var_name in self._custom_vars: |
207 return self._custom_vars[var_name] | 184 return self._custom_vars[var_name] |
208 elif var_name in self._local_scope.get("vars", {}): | 185 elif var_name in self._local_scope.get("vars", {}): |
(...skipping 28 matching lines...) Expand all Loading... |
237 self._relative = relative | 214 self._relative = relative |
238 # This is a mutable value which has the list of 'target_os' OSes listed in | 215 # This is a mutable value which has the list of 'target_os' OSes listed in |
239 # the current deps file. | 216 # the current deps file. |
240 self.local_target_os = None | 217 self.local_target_os = None |
241 | 218 |
242 # These are only set in .gclient and not in DEPS files. | 219 # These are only set in .gclient and not in DEPS files. |
243 self._custom_vars = custom_vars or {} | 220 self._custom_vars = custom_vars or {} |
244 self._custom_deps = custom_deps or {} | 221 self._custom_deps = custom_deps or {} |
245 self._custom_hooks = custom_hooks or [] | 222 self._custom_hooks = custom_hooks or [] |
246 | 223 |
247 # TODO(iannucci): Remove this when all masters are correctly substituting | |
248 # the new blink url. | |
249 if (self._custom_vars.get('webkit_trunk', '') == | |
250 'svn://svn-mirror.golo.chromium.org/webkit-readonly/trunk'): | |
251 new_url = 'svn://svn-mirror.golo.chromium.org/blink/trunk' | |
252 print('Overwriting Var("webkit_trunk") with %s' % new_url) | |
253 self._custom_vars['webkit_trunk'] = new_url | |
254 | |
255 # Post process the url to remove trailing slashes. | 224 # Post process the url to remove trailing slashes. |
256 if isinstance(self._url, basestring): | 225 if isinstance(self._url, basestring): |
257 # urls are sometime incorrectly written as proto://host/path/@rev. Replace | 226 # urls are sometime incorrectly written as proto://host/path/@rev. Replace |
258 # it to proto://host/path@rev. | 227 # it to proto://host/path@rev. |
259 self._url = self._url.replace('/@', '@') | 228 self._url = self._url.replace('/@', '@') |
260 elif not isinstance(self._url, | 229 elif not isinstance(self._url, (self.FromImpl, None.__class__)): |
261 (self.FromImpl, self.FileImpl, None.__class__)): | |
262 raise gclient_utils.Error( | 230 raise gclient_utils.Error( |
263 ('dependency url must be either a string, None, ' | 231 ('dependency url must be either a string, None, ' |
264 'File() or From() instead of %s') % self._url.__class__.__name__) | 232 'or From() instead of %s') % self._url.__class__.__name__) |
265 # Make any deps_file path platform-appropriate. | 233 # Make any deps_file path platform-appropriate. |
266 for sep in ['/', '\\']: | 234 for sep in ['/', '\\']: |
267 self._deps_file = self._deps_file.replace(sep, os.sep) | 235 self._deps_file = self._deps_file.replace(sep, os.sep) |
268 | 236 |
269 @property | 237 @property |
270 def deps_file(self): | 238 def deps_file(self): |
271 return self._deps_file | 239 return self._deps_file |
272 | 240 |
273 @property | 241 @property |
274 def managed(self): | 242 def managed(self): |
(...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
527 parsed_url = urlparse.urlparse(url) | 495 parsed_url = urlparse.urlparse(url) |
528 if (not parsed_url[0] and | 496 if (not parsed_url[0] and |
529 not re.match(r'^\w+\@[\w\.-]+\:[\w\/]+', parsed_url[2])): | 497 not re.match(r'^\w+\@[\w\.-]+\:[\w\/]+', parsed_url[2])): |
530 # A relative url. Fetch the real base. | 498 # A relative url. Fetch the real base. |
531 path = parsed_url[2] | 499 path = parsed_url[2] |
532 if not path.startswith('/'): | 500 if not path.startswith('/'): |
533 raise gclient_utils.Error( | 501 raise gclient_utils.Error( |
534 'relative DEPS entry \'%s\' must begin with a slash' % url) | 502 'relative DEPS entry \'%s\' must begin with a slash' % url) |
535 # Create a scm just to query the full url. | 503 # Create a scm just to query the full url. |
536 parent_url = self.parent.parsed_url | 504 parent_url = self.parent.parsed_url |
537 if isinstance(parent_url, self.FileImpl): | |
538 parent_url = parent_url.file_location | |
539 scm = gclient_scm.CreateSCM( | 505 scm = gclient_scm.CreateSCM( |
540 parent_url, self.root.root_dir, None, self.outbuf) | 506 parent_url, self.root.root_dir, None, self.outbuf) |
541 parsed_url = scm.FullUrlForRelativeUrl(url) | 507 parsed_url = scm.FullUrlForRelativeUrl(url) |
542 else: | 508 else: |
543 parsed_url = url | 509 parsed_url = url |
544 logging.info( | 510 logging.info( |
545 'Dependency(%s).LateOverride(%s) -> %s' % | 511 'Dependency(%s).LateOverride(%s) -> %s' % |
546 (self.name, url, parsed_url)) | 512 (self.name, url, parsed_url)) |
547 return parsed_url | 513 return parsed_url |
548 | 514 |
549 if isinstance(url, self.FileImpl): | |
550 logging.info( | |
551 'Dependency(%s).LateOverride(%s) -> %s (File)' % | |
552 (self.name, url, url)) | |
553 return url | |
554 | |
555 if url is None: | 515 if url is None: |
556 logging.info( | 516 logging.info( |
557 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url)) | 517 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url)) |
558 return url | 518 return url |
559 | 519 |
560 raise gclient_utils.Error('Unknown url type') | 520 raise gclient_utils.Error('Unknown url type') |
561 | 521 |
562 @staticmethod | 522 @staticmethod |
563 def MergeWithOsDeps(deps, deps_os, target_os_list): | 523 def MergeWithOsDeps(deps, deps_os, target_os_list): |
564 """Returns a new "deps" structure that is the deps sent in updated | 524 """Returns a new "deps" structure that is the deps sent in updated |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
635 if use_strict: | 595 if use_strict: |
636 logging.info( | 596 logging.info( |
637 'ParseDepsFile(%s): Strict Mode Enabled', self.name) | 597 'ParseDepsFile(%s): Strict Mode Enabled', self.name) |
638 global_scope = { | 598 global_scope = { |
639 '__builtins__': {'None': None}, | 599 '__builtins__': {'None': None}, |
640 'Var': var.Lookup, | 600 'Var': var.Lookup, |
641 'deps_os': {}, | 601 'deps_os': {}, |
642 } | 602 } |
643 else: | 603 else: |
644 global_scope = { | 604 global_scope = { |
645 'File': self.FileImpl, | |
646 'From': self.FromImpl, | 605 'From': self.FromImpl, |
647 'Var': var.Lookup, | 606 'Var': var.Lookup, |
648 'deps_os': {}, | 607 'deps_os': {}, |
649 } | 608 } |
650 # Eval the content. | 609 # Eval the content. |
651 try: | 610 try: |
652 exec(deps_content, global_scope, local_scope) | 611 exec(deps_content, global_scope, local_scope) |
653 except SyntaxError as e: | 612 except SyntaxError as e: |
654 gclient_utils.SyntaxErrorToError(filepath, e) | 613 gclient_utils.SyntaxErrorToError(filepath, e) |
655 if use_strict: | 614 if use_strict: |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
766 self.add_dependencies_and_close(deps_to_add, hooks_to_run) | 725 self.add_dependencies_and_close(deps_to_add, hooks_to_run) |
767 logging.info('ParseDepsFile(%s) done' % self.name) | 726 logging.info('ParseDepsFile(%s) done' % self.name) |
768 | 727 |
769 def add_dependencies_and_close(self, deps_to_add, hooks): | 728 def add_dependencies_and_close(self, deps_to_add, hooks): |
770 """Adds the dependencies, hooks and mark the parsing as done.""" | 729 """Adds the dependencies, hooks and mark the parsing as done.""" |
771 for dep in deps_to_add: | 730 for dep in deps_to_add: |
772 if dep.verify_validity(): | 731 if dep.verify_validity(): |
773 self.add_dependency(dep) | 732 self.add_dependency(dep) |
774 self._mark_as_parsed(hooks) | 733 self._mark_as_parsed(hooks) |
775 | 734 |
776 def maybeGetParentRevision(self, command, options, parsed_url, parent): | |
777 """Uses revision/timestamp of parent if no explicit revision was specified. | |
778 | |
779 If we are performing an update and --transitive is set, use | |
780 - the parent's revision if 'self.url' is in the same repository | |
781 - the parent's timestamp otherwise | |
782 to update 'self.url'. The used revision/timestamp will be set in | |
783 'options.revision'. | |
784 If we have an explicit revision do nothing. | |
785 """ | |
786 if command == 'update' and options.transitive and not options.revision: | |
787 _, revision = gclient_utils.SplitUrlRevision(parsed_url) | |
788 if not revision: | |
789 options.revision = getattr(parent, '_used_revision', None) | |
790 if (options.revision and | |
791 not gclient_utils.IsDateRevision(options.revision)): | |
792 assert self.parent and self.parent.used_scm | |
793 # If this dependency is in the same repository as parent it's url will | |
794 # start with a slash. If so we take the parent revision instead of | |
795 # it's timestamp. | |
796 # (The timestamps of commits in google code are broken -- which can | |
797 # result in dependencies to be checked out at the wrong revision) | |
798 if self.url.startswith('/'): | |
799 if options.verbose: | |
800 print('Using parent\'s revision %s since we are in the same ' | |
801 'repository.' % options.revision) | |
802 else: | |
803 parent_revision_date = self.parent.used_scm.GetRevisionDate( | |
804 options.revision) | |
805 options.revision = gclient_utils.MakeDateRevision( | |
806 parent_revision_date) | |
807 if options.verbose: | |
808 print('Using parent\'s revision date %s since we are in a ' | |
809 'different repository.' % options.revision) | |
810 | |
811 def findDepsFromNotAllowedHosts(self): | 735 def findDepsFromNotAllowedHosts(self): |
812 """Returns a list of depenecies from not allowed hosts. | 736 """Returns a list of depenecies from not allowed hosts. |
813 | 737 |
814 If allowed_hosts is not set, allows all hosts and returns empty list. | 738 If allowed_hosts is not set, allows all hosts and returns empty list. |
815 """ | 739 """ |
816 if not self._allowed_hosts: | 740 if not self._allowed_hosts: |
817 return [] | 741 return [] |
818 bad_deps = [] | 742 bad_deps = [] |
819 for dep in self._dependencies: | 743 for dep in self._dependencies: |
820 # Don't enforce this for custom_deps. | 744 # Don't enforce this for custom_deps. |
(...skipping 14 matching lines...) Expand all Loading... |
835 if not self.should_process: | 759 if not self.should_process: |
836 return | 760 return |
837 # When running runhooks, there's no need to consult the SCM. | 761 # When running runhooks, there's no need to consult the SCM. |
838 # All known hooks are expected to run unconditionally regardless of working | 762 # All known hooks are expected to run unconditionally regardless of working |
839 # copy state, so skip the SCM status check. | 763 # copy state, so skip the SCM status check. |
840 run_scm = command not in ('runhooks', 'recurse', None) | 764 run_scm = command not in ('runhooks', 'recurse', None) |
841 parsed_url = self.LateOverride(self.url) | 765 parsed_url = self.LateOverride(self.url) |
842 file_list = [] if not options.nohooks else None | 766 file_list = [] if not options.nohooks else None |
843 revision_override = revision_overrides.pop(self.name, None) | 767 revision_override = revision_overrides.pop(self.name, None) |
844 if run_scm and parsed_url: | 768 if run_scm and parsed_url: |
845 if isinstance(parsed_url, self.FileImpl): | 769 # Create a shallow copy to mutate revision. |
846 # Special support for single-file checkout. | 770 options = copy.copy(options) |
847 if not command in (None, 'cleanup', 'diff', 'pack', 'status'): | 771 options.revision = revision_override |
848 # Sadly, pylint doesn't realize that parsed_url is of FileImpl. | 772 self._used_revision = options.revision |
849 # pylint: disable=E1103 | 773 self._used_scm = gclient_scm.CreateSCM( |
850 options.revision = parsed_url.GetRevision() | 774 parsed_url, self.root.root_dir, self.name, self.outbuf, |
851 self._used_scm = gclient_scm.SVNWrapper( | 775 out_cb=work_queue.out_cb) |
852 parsed_url.GetPath(), self.root.root_dir, self.name, | 776 self._got_revision = self._used_scm.RunCommand(command, options, args, |
853 out_cb=work_queue.out_cb) | 777 file_list) |
854 self._used_scm.RunCommand('updatesingle', | 778 if file_list: |
855 options, args + [parsed_url.GetFilename()], file_list) | 779 file_list = [os.path.join(self.name, f.strip()) for f in file_list] |
856 else: | |
857 # Create a shallow copy to mutate revision. | |
858 options = copy.copy(options) | |
859 options.revision = revision_override | |
860 self.maybeGetParentRevision( | |
861 command, options, parsed_url, self.parent) | |
862 self._used_revision = options.revision | |
863 self._used_scm = gclient_scm.CreateSCM( | |
864 parsed_url, self.root.root_dir, self.name, self.outbuf, | |
865 out_cb=work_queue.out_cb) | |
866 self._got_revision = self._used_scm.RunCommand(command, options, args, | |
867 file_list) | |
868 if file_list: | |
869 file_list = [os.path.join(self.name, f.strip()) for f in file_list] | |
870 | 780 |
871 # TODO(phajdan.jr): We should know exactly when the paths are absolute. | 781 # TODO(phajdan.jr): We should know exactly when the paths are absolute. |
872 # Convert all absolute paths to relative. | 782 # Convert all absolute paths to relative. |
873 for i in range(len(file_list or [])): | 783 for i in range(len(file_list or [])): |
874 # It depends on the command being executed (like runhooks vs sync). | 784 # It depends on the command being executed (like runhooks vs sync). |
875 if not os.path.isabs(file_list[i]): | 785 if not os.path.isabs(file_list[i]): |
876 continue | 786 continue |
877 prefix = os.path.commonprefix( | 787 prefix = os.path.commonprefix( |
878 [self.root.root_dir.lower(), file_list[i].lower()]) | 788 [self.root.root_dir.lower(), file_list[i].lower()]) |
879 file_list[i] = file_list[i][len(prefix):] | 789 file_list[i] = file_list[i][len(prefix):] |
880 # Strip any leading path separators. | 790 # Strip any leading path separators. |
881 while file_list[i].startswith(('\\', '/')): | 791 while file_list[i].startswith(('\\', '/')): |
882 file_list[i] = file_list[i][1:] | 792 file_list[i] = file_list[i][1:] |
883 | 793 |
884 # Always parse the DEPS file. | 794 # Always parse the DEPS file. |
885 self.ParseDepsFile() | 795 self.ParseDepsFile() |
886 self._run_is_done(file_list or [], parsed_url) | 796 self._run_is_done(file_list or [], parsed_url) |
887 if command in ('update', 'revert') and not options.noprehooks: | 797 if command in ('update', 'revert') and not options.noprehooks: |
888 self.RunPreDepsHooks() | 798 self.RunPreDepsHooks() |
889 | 799 |
890 if self.recursion_limit: | 800 if self.recursion_limit: |
891 # Parse the dependencies of this dependency. | 801 # Parse the dependencies of this dependency. |
892 for s in self.dependencies: | 802 for s in self.dependencies: |
893 work_queue.enqueue(s) | 803 work_queue.enqueue(s) |
894 | 804 |
895 if command == 'recurse': | 805 if command == 'recurse': |
896 if not isinstance(parsed_url, self.FileImpl): | 806 # Skip file only checkout. |
897 # Skip file only checkout. | 807 scm = gclient_scm.GetScmName(parsed_url) |
898 scm = gclient_scm.GetScmName(parsed_url) | 808 if not options.scm or scm in options.scm: |
899 if not options.scm or scm in options.scm: | 809 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name)) |
900 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name)) | 810 # Pass in the SCM type as an env variable. Make sure we don't put |
901 # Pass in the SCM type as an env variable. Make sure we don't put | 811 # unicode strings in the environment. |
902 # unicode strings in the environment. | 812 env = os.environ.copy() |
903 env = os.environ.copy() | 813 if scm: |
904 if scm: | 814 env['GCLIENT_SCM'] = str(scm) |
905 env['GCLIENT_SCM'] = str(scm) | 815 if parsed_url: |
906 if parsed_url: | 816 env['GCLIENT_URL'] = str(parsed_url) |
907 env['GCLIENT_URL'] = str(parsed_url) | 817 env['GCLIENT_DEP_PATH'] = str(self.name) |
908 env['GCLIENT_DEP_PATH'] = str(self.name) | 818 if options.prepend_dir and scm == 'git': |
909 if options.prepend_dir and scm == 'git': | 819 print_stdout = False |
910 print_stdout = False | 820 def filter_fn(line): |
911 def filter_fn(line): | 821 """Git-specific path marshaling. It is optimized for git-grep.""" |
912 """Git-specific path marshaling. It is optimized for git-grep.""" | |
913 | 822 |
914 def mod_path(git_pathspec): | 823 def mod_path(git_pathspec): |
915 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec) | 824 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec) |
916 modified_path = os.path.join(self.name, match.group(2)) | 825 modified_path = os.path.join(self.name, match.group(2)) |
917 branch = match.group(1) or '' | 826 branch = match.group(1) or '' |
918 return '%s%s' % (branch, modified_path) | 827 return '%s%s' % (branch, modified_path) |
919 | 828 |
920 match = re.match('^Binary file ([^\0]+) matches$', line) | 829 match = re.match('^Binary file ([^\0]+) matches$', line) |
921 if match: | 830 if match: |
922 print('Binary file %s matches\n' % mod_path(match.group(1))) | 831 print('Binary file %s matches\n' % mod_path(match.group(1))) |
923 return | 832 return |
924 | 833 |
925 items = line.split('\0') | 834 items = line.split('\0') |
926 if len(items) == 2 and items[1]: | 835 if len(items) == 2 and items[1]: |
927 print('%s : %s' % (mod_path(items[0]), items[1])) | 836 print('%s : %s' % (mod_path(items[0]), items[1])) |
928 elif len(items) >= 2: | 837 elif len(items) >= 2: |
929 # Multiple null bytes or a single trailing null byte indicate | 838 # Multiple null bytes or a single trailing null byte indicate |
930 # git is likely displaying filenames only (such as with -l) | 839 # git is likely displaying filenames only (such as with -l) |
931 print('\n'.join(mod_path(path) for path in items if path)) | 840 print('\n'.join(mod_path(path) for path in items if path)) |
932 else: | 841 else: |
933 print(line) | 842 print(line) |
934 else: | 843 else: |
935 print_stdout = True | 844 print_stdout = True |
936 filter_fn = None | 845 filter_fn = None |
937 | 846 |
938 if parsed_url is None: | 847 if parsed_url is None: |
939 print('Skipped omitted dependency %s' % cwd, file=sys.stderr) | 848 print('Skipped omitted dependency %s' % cwd, file=sys.stderr) |
940 elif os.path.isdir(cwd): | 849 elif os.path.isdir(cwd): |
941 try: | 850 try: |
942 gclient_utils.CheckCallAndFilter( | 851 gclient_utils.CheckCallAndFilter( |
943 args, cwd=cwd, env=env, print_stdout=print_stdout, | 852 args, cwd=cwd, env=env, print_stdout=print_stdout, |
944 filter_fn=filter_fn, | 853 filter_fn=filter_fn, |
945 ) | 854 ) |
946 except subprocess2.CalledProcessError: | 855 except subprocess2.CalledProcessError: |
947 if not options.ignore: | 856 if not options.ignore: |
948 raise | 857 raise |
949 else: | 858 else: |
950 print('Skipped missing %s' % cwd, file=sys.stderr) | 859 print('Skipped missing %s' % cwd, file=sys.stderr) |
951 | 860 |
952 | 861 |
953 @gclient_utils.lockedmethod | 862 @gclient_utils.lockedmethod |
954 def _run_is_done(self, file_list, parsed_url): | 863 def _run_is_done(self, file_list, parsed_url): |
955 # Both these are kept for hooks that are run as a separate tree traversal. | 864 # Both these are kept for hooks that are run as a separate tree traversal. |
956 self._file_list = file_list | 865 self._file_list = file_list |
957 self._parsed_url = parsed_url | 866 self._parsed_url = parsed_url |
958 self._processed = True | 867 self._processed = True |
959 | 868 |
960 @staticmethod | 869 @staticmethod |
(...skipping 17 matching lines...) Expand all Loading... |
978 | 887 |
979 RunOnDeps() must have been called before to load the DEPS. | 888 RunOnDeps() must have been called before to load the DEPS. |
980 """ | 889 """ |
981 result = [] | 890 result = [] |
982 if not self.should_process or not self.recursion_limit: | 891 if not self.should_process or not self.recursion_limit: |
983 # Don't run the hook when it is above recursion_limit. | 892 # Don't run the hook when it is above recursion_limit. |
984 return result | 893 return result |
985 # If "--force" was specified, run all hooks regardless of what files have | 894 # If "--force" was specified, run all hooks regardless of what files have |
986 # changed. | 895 # changed. |
987 if self.deps_hooks: | 896 if self.deps_hooks: |
988 # TODO(maruel): If the user is using git or git-svn, then we don't know | 897 # TODO(maruel): If the user is using git, then we don't know |
989 # what files have changed so we always run all hooks. It'd be nice to fix | 898 # what files have changed so we always run all hooks. It'd be nice to fix |
990 # that. | 899 # that. |
991 if (options.force or | 900 if (options.force or |
992 isinstance(self.parsed_url, self.FileImpl) or | |
993 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or | 901 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or |
994 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))): | 902 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))): |
995 for hook_dict in self.deps_hooks: | 903 for hook_dict in self.deps_hooks: |
996 result.append(self.GetHookAction(hook_dict, [])) | 904 result.append(self.GetHookAction(hook_dict, [])) |
997 else: | 905 else: |
998 # Run hooks on the basis of whether the files from the gclient operation | 906 # Run hooks on the basis of whether the files from the gclient operation |
999 # match each hook's pattern. | 907 # match each hook's pattern. |
1000 for hook_dict in self.deps_hooks: | 908 for hook_dict in self.deps_hooks: |
1001 pattern = re.compile(hook_dict['pattern']) | 909 pattern = re.compile(hook_dict['pattern']) |
1002 matching_file_list = [ | 910 matching_file_list = [ |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1266 is actually checked out in %(checkout_path)s. | 1174 is actually checked out in %(checkout_path)s. |
1267 | 1175 |
1268 The .gclient file contains: | 1176 The .gclient file contains: |
1269 URL: %(expected_url)s (%(expected_scm)s) | 1177 URL: %(expected_url)s (%(expected_scm)s) |
1270 Cache mirror: %(mirror_string)s | 1178 Cache mirror: %(mirror_string)s |
1271 | 1179 |
1272 The local checkout in %(checkout_path)s reports: | 1180 The local checkout in %(checkout_path)s reports: |
1273 %(actual_url)s (%(actual_scm)s) | 1181 %(actual_url)s (%(actual_scm)s) |
1274 | 1182 |
1275 You should ensure that the URL listed in .gclient is correct and either change | 1183 You should ensure that the URL listed in .gclient is correct and either change |
1276 it or fix the checkout. If you're managing your own git checkout in | 1184 it or fix the checkout. |
1277 %(checkout_path)s but the URL in .gclient is for an svn repository, you probably | |
1278 want to set 'managed': False in .gclient. | |
1279 ''' % {'checkout_path': os.path.join(self.root_dir, dep.name), | 1185 ''' % {'checkout_path': os.path.join(self.root_dir, dep.name), |
1280 'expected_url': dep.url, | 1186 'expected_url': dep.url, |
1281 'expected_scm': gclient_scm.GetScmName(dep.url), | 1187 'expected_scm': gclient_scm.GetScmName(dep.url), |
1282 'mirror_string' : mirror_string, | 1188 'mirror_string' : mirror_string, |
1283 'actual_url': actual_url, | 1189 'actual_url': actual_url, |
1284 'actual_scm': gclient_scm.GetScmName(actual_url)}) | 1190 'actual_scm': gclient_scm.GetScmName(actual_url)}) |
1285 | 1191 |
1286 def SetConfig(self, content): | 1192 def SetConfig(self, content): |
1287 assert not self.dependencies | 1193 assert not self.dependencies |
1288 config_dict = {} | 1194 config_dict = {} |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1331 raise gclient_utils.Error('Invalid .gclient file. Solution is ' | 1237 raise gclient_utils.Error('Invalid .gclient file. Solution is ' |
1332 'incomplete: %s' % s) | 1238 'incomplete: %s' % s) |
1333 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', [])) | 1239 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', [])) |
1334 logging.info('SetConfig() done') | 1240 logging.info('SetConfig() done') |
1335 | 1241 |
1336 def SaveConfig(self): | 1242 def SaveConfig(self): |
1337 gclient_utils.FileWrite(os.path.join(self.root_dir, | 1243 gclient_utils.FileWrite(os.path.join(self.root_dir, |
1338 self._options.config_filename), | 1244 self._options.config_filename), |
1339 self.config_content) | 1245 self.config_content) |
1340 | 1246 |
1341 def MigrateConfigToGit(self, path, options): | |
1342 svn_url_re = re.compile('^(https?://src\.chromium\.org/svn|' | |
1343 'svn://svn\.chromium\.org/chrome)/' | |
1344 '(trunk|branches/[^/]+)/src') | |
1345 old_git_re = re.compile('^(https?://git\.chromium\.org|' | |
1346 'ssh://([a-zA-Z_][a-zA-Z0-9_-]*@)?' | |
1347 'gerrit\.chromium\.org(:2941[89])?)/' | |
1348 'chromium/src\.git') | |
1349 # Scan existing .gclient file for obsolete settings. It would be simpler | |
1350 # to traverse self.dependencies, but working with the AST allows the code to | |
1351 # dump an updated .gclient file that preserves the ordering of the original. | |
1352 a = ast.parse(self.config_content, options.config_filename, 'exec') | |
1353 modified = False | |
1354 solutions = [elem for elem in a.body if 'solutions' in | |
1355 [target.id for target in elem.targets]] | |
1356 if not solutions: | |
1357 return self | |
1358 solutions = solutions[-1] | |
1359 for solution in solutions.value.elts: | |
1360 # Check for obsolete URL's | |
1361 url_idx = ast_dict_index(solution, 'url') | |
1362 if url_idx == -1: | |
1363 continue | |
1364 url_val = solution.values[url_idx] | |
1365 if type(url_val) is not ast.Str: | |
1366 continue | |
1367 if (svn_url_re.match(url_val.s.strip())): | |
1368 raise gclient_utils.Error( | |
1369 """ | |
1370 The chromium code repository has migrated completely to git. | |
1371 Your SVN-based checkout is now obsolete; you need to create a brand-new | |
1372 git checkout by following these instructions: | |
1373 | |
1374 http://www.chromium.org/developers/how-tos/get-the-code | |
1375 """) | |
1376 if (old_git_re.match(url_val.s.strip())): | |
1377 url_val.s = CHROMIUM_SRC_URL | |
1378 modified = True | |
1379 | |
1380 # Ensure deps_file is set to .DEPS.git. We enforce this here to smooth | |
1381 # over switching between pre-git-migration and post-git-migration | |
1382 # revisions. | |
1383 # - For pre-migration revisions, .DEPS.git must be explicitly set. | |
1384 # - For post-migration revisions, .DEPS.git is not present, so gclient | |
1385 # will correctly fall back to DEPS. | |
1386 if url_val.s == CHROMIUM_SRC_URL: | |
1387 deps_file_idx = ast_dict_index(solution, 'deps_file') | |
1388 if deps_file_idx != -1: | |
1389 continue | |
1390 solution.keys.append(ast.Str('deps_file')) | |
1391 solution.values.append(ast.Str('.DEPS.git')) | |
1392 modified = True | |
1393 | |
1394 if not modified: | |
1395 return self | |
1396 | |
1397 print( | |
1398 """ | |
1399 WARNING: gclient detected an obsolete setting in your %s file. The file has | |
1400 been automagically updated. The previous version is available at %s.old. | |
1401 """ % (options.config_filename, options.config_filename)) | |
1402 | |
1403 # Replace existing .gclient with the updated version. | |
1404 # Return a new GClient instance based on the new content. | |
1405 new_content = ast2str(a) | |
1406 dot_gclient_fn = os.path.join(path, options.config_filename) | |
1407 try: | |
1408 os.rename(dot_gclient_fn, dot_gclient_fn + '.old') | |
1409 except OSError: | |
1410 pass | |
1411 with open(dot_gclient_fn, 'w') as fh: | |
1412 fh.write(new_content) | |
1413 client = GClient(path, options) | |
1414 client.SetConfig(new_content) | |
1415 return client | |
1416 | |
1417 @staticmethod | 1247 @staticmethod |
1418 def LoadCurrentConfig(options): | 1248 def LoadCurrentConfig(options): |
1419 """Searches for and loads a .gclient file relative to the current working | 1249 """Searches for and loads a .gclient file relative to the current working |
1420 dir. Returns a GClient object.""" | 1250 dir. Returns a GClient object.""" |
1421 if options.spec: | 1251 if options.spec: |
1422 client = GClient('.', options) | 1252 client = GClient('.', options) |
1423 client.SetConfig(options.spec) | 1253 client.SetConfig(options.spec) |
1424 else: | 1254 else: |
1425 if options.verbose: | 1255 if options.verbose: |
1426 print('Looking for %s starting from %s\n' % ( | 1256 print('Looking for %s starting from %s\n' % ( |
1427 options.config_filename, os.getcwd())) | 1257 options.config_filename, os.getcwd())) |
1428 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename) | 1258 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename) |
1429 if not path: | 1259 if not path: |
1430 return None | 1260 return None |
1431 client = GClient(path, options) | 1261 client = GClient(path, options) |
1432 client.SetConfig(gclient_utils.FileRead( | 1262 client.SetConfig(gclient_utils.FileRead( |
1433 os.path.join(path, options.config_filename))) | 1263 os.path.join(path, options.config_filename))) |
1434 client = client.MigrateConfigToGit(path, options) | |
1435 | 1264 |
1436 if (options.revisions and | 1265 if (options.revisions and |
1437 len(client.dependencies) > 1 and | 1266 len(client.dependencies) > 1 and |
1438 any('@' not in r for r in options.revisions)): | 1267 any('@' not in r for r in options.revisions)): |
1439 print( | 1268 print( |
1440 ('You must specify the full solution name like --revision %s@%s\n' | 1269 ('You must specify the full solution name like --revision %s@%s\n' |
1441 'when you have multiple solutions setup in your .gclient file.\n' | 1270 'when you have multiple solutions setup in your .gclient file.\n' |
1442 'Other solutions present are: %s.') % ( | 1271 'Other solutions present are: %s.') % ( |
1443 client.dependencies[0].name, | 1272 client.dependencies[0].name, |
1444 options.revisions[0], | 1273 options.revisions[0], |
(...skipping 14 matching lines...) Expand all Loading... |
1459 | 1288 |
1460 def _SaveEntries(self): | 1289 def _SaveEntries(self): |
1461 """Creates a .gclient_entries file to record the list of unique checkouts. | 1290 """Creates a .gclient_entries file to record the list of unique checkouts. |
1462 | 1291 |
1463 The .gclient_entries file lives in the same directory as .gclient. | 1292 The .gclient_entries file lives in the same directory as .gclient. |
1464 """ | 1293 """ |
1465 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It | 1294 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It |
1466 # makes testing a bit too fun. | 1295 # makes testing a bit too fun. |
1467 result = 'entries = {\n' | 1296 result = 'entries = {\n' |
1468 for entry in self.root.subtree(False): | 1297 for entry in self.root.subtree(False): |
1469 # Skip over File() dependencies as we can't version them. | 1298 result += ' %s: %s,\n' % (pprint.pformat(entry.name), |
1470 if not isinstance(entry.parsed_url, self.FileImpl): | 1299 pprint.pformat(entry.parsed_url)) |
1471 result += ' %s: %s,\n' % (pprint.pformat(entry.name), | |
1472 pprint.pformat(entry.parsed_url)) | |
1473 result += '}\n' | 1300 result += '}\n' |
1474 file_path = os.path.join(self.root_dir, self._options.entries_filename) | 1301 file_path = os.path.join(self.root_dir, self._options.entries_filename) |
1475 logging.debug(result) | 1302 logging.debug(result) |
1476 gclient_utils.FileWrite(file_path, result) | 1303 gclient_utils.FileWrite(file_path, result) |
1477 | 1304 |
1478 def _ReadEntries(self): | 1305 def _ReadEntries(self): |
1479 """Read the .gclient_entries file for the given client. | 1306 """Read the .gclient_entries file for the given client. |
1480 | 1307 |
1481 Returns: | 1308 Returns: |
1482 A sequence of solution names, which will be empty if there is the | 1309 A sequence of solution names, which will be empty if there is the |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1592 e_dir = os.path.join(self.root_dir, entry_fixed) | 1419 e_dir = os.path.join(self.root_dir, entry_fixed) |
1593 # Use entry and not entry_fixed there. | 1420 # Use entry and not entry_fixed there. |
1594 if (entry not in entries and | 1421 if (entry not in entries and |
1595 (not any(path.startswith(entry + '/') for path in entries)) and | 1422 (not any(path.startswith(entry + '/') for path in entries)) and |
1596 os.path.exists(e_dir)): | 1423 os.path.exists(e_dir)): |
1597 # The entry has been removed from DEPS. | 1424 # The entry has been removed from DEPS. |
1598 scm = gclient_scm.CreateSCM( | 1425 scm = gclient_scm.CreateSCM( |
1599 prev_url, self.root_dir, entry_fixed, self.outbuf) | 1426 prev_url, self.root_dir, entry_fixed, self.outbuf) |
1600 | 1427 |
1601 # Check to see if this directory is now part of a higher-up checkout. | 1428 # Check to see if this directory is now part of a higher-up checkout. |
1602 # The directory might be part of a git OR svn checkout. | |
1603 scm_root = None | 1429 scm_root = None |
1604 scm_class = None | 1430 try: |
1605 for scm_class in (gclient_scm.scm.GIT, gclient_scm.scm.SVN): | 1431 scm_root = gclient_scm.scm.GIT.GetCheckoutRoot(scm.checkout_path) |
1606 try: | 1432 except subprocess2.CalledProcessError: |
1607 scm_root = scm_class.GetCheckoutRoot(scm.checkout_path) | 1433 pass |
1608 except subprocess2.CalledProcessError: | 1434 if not scm_root: |
1609 pass | |
1610 if scm_root: | |
1611 break | |
1612 else: | |
1613 logging.warning('Could not find checkout root for %s. Unable to ' | 1435 logging.warning('Could not find checkout root for %s. Unable to ' |
1614 'determine whether it is part of a higher-level ' | 1436 'determine whether it is part of a higher-level ' |
1615 'checkout, so not removing.' % entry) | 1437 'checkout, so not removing.' % entry) |
1616 continue | 1438 continue |
1617 | 1439 |
1618 # This is to handle the case of third_party/WebKit migrating from | 1440 # This is to handle the case of third_party/WebKit migrating from |
1619 # being a DEPS entry to being part of the main project. | 1441 # being a DEPS entry to being part of the main project. |
1620 # If the subproject is a Git project, we need to remove its .git | 1442 # If the subproject is a Git project, we need to remove its .git |
1621 # folder. Otherwise git operations on that folder will have different | 1443 # folder. Otherwise git operations on that folder will have different |
1622 # effects depending on the current working directory. | 1444 # effects depending on the current working directory. |
1623 if scm_class == gclient_scm.scm.GIT and ( | 1445 if os.path.abspath(scm_root) == os.path.abspath(e_dir): |
1624 os.path.abspath(scm_root) == os.path.abspath(e_dir)): | |
1625 e_par_dir = os.path.join(e_dir, os.pardir) | 1446 e_par_dir = os.path.join(e_dir, os.pardir) |
1626 if scm_class.IsInsideWorkTree(e_par_dir): | 1447 if gclient_scm.scm.GIT.IsInsideWorkTree(e_par_dir): |
1627 par_scm_root = scm_class.GetCheckoutRoot(e_par_dir) | 1448 par_scm_root = gclient_scm.scm.GIT.GetCheckoutRoot(e_par_dir) |
1628 # rel_e_dir : relative path of entry w.r.t. its parent repo. | 1449 # rel_e_dir : relative path of entry w.r.t. its parent repo. |
1629 rel_e_dir = os.path.relpath(e_dir, par_scm_root) | 1450 rel_e_dir = os.path.relpath(e_dir, par_scm_root) |
1630 if scm_class.IsDirectoryVersioned(par_scm_root, rel_e_dir): | 1451 if gclient_scm.scm.GIT.IsDirectoryVersioned( |
| 1452 par_scm_root, rel_e_dir): |
1631 save_dir = scm.GetGitBackupDirPath() | 1453 save_dir = scm.GetGitBackupDirPath() |
1632 # Remove any eventual stale backup dir for the same project. | 1454 # Remove any eventual stale backup dir for the same project. |
1633 if os.path.exists(save_dir): | 1455 if os.path.exists(save_dir): |
1634 gclient_utils.rmtree(save_dir) | 1456 gclient_utils.rmtree(save_dir) |
1635 os.rename(os.path.join(e_dir, '.git'), save_dir) | 1457 os.rename(os.path.join(e_dir, '.git'), save_dir) |
1636 # When switching between the two states (entry/ is a subproject | 1458 # When switching between the two states (entry/ is a subproject |
1637 # -> entry/ is part of the outer project), it is very likely | 1459 # -> entry/ is part of the outer project), it is very likely |
1638 # that some files are changed in the checkout, unless we are | 1460 # that some files are changed in the checkout, unless we are |
1639 # jumping *exactly* across the commit which changed just DEPS. | 1461 # jumping *exactly* across the commit which changed just DEPS. |
1640 # In such case we want to cleanup any eventual stale files | 1462 # In such case we want to cleanup any eventual stale files |
1641 # (coming from the old subproject) in order to end up with a | 1463 # (coming from the old subproject) in order to end up with a |
1642 # clean checkout. | 1464 # clean checkout. |
1643 scm_class.CleanupDir(par_scm_root, rel_e_dir) | 1465 gclient_scm.scm.GIT.CleanupDir(par_scm_root, rel_e_dir) |
1644 assert not os.path.exists(os.path.join(e_dir, '.git')) | 1466 assert not os.path.exists(os.path.join(e_dir, '.git')) |
1645 print(('\nWARNING: \'%s\' has been moved from DEPS to a higher ' | 1467 print(('\nWARNING: \'%s\' has been moved from DEPS to a higher ' |
1646 'level checkout. The git folder containing all the local' | 1468 'level checkout. The git folder containing all the local' |
1647 ' branches has been saved to %s.\n' | 1469 ' branches has been saved to %s.\n' |
1648 'If you don\'t care about its state you can safely ' | 1470 'If you don\'t care about its state you can safely ' |
1649 'remove that folder to free up space.') % | 1471 'remove that folder to free up space.') % |
1650 (entry, save_dir)) | 1472 (entry, save_dir)) |
1651 continue | 1473 continue |
1652 | 1474 |
1653 if scm_root in full_entries: | 1475 if scm_root in full_entries: |
(...skipping 27 matching lines...) Expand all Loading... |
1681 work_queue = gclient_utils.ExecutionQueue( | 1503 work_queue = gclient_utils.ExecutionQueue( |
1682 self._options.jobs, None, False, verbose=self._options.verbose) | 1504 self._options.jobs, None, False, verbose=self._options.verbose) |
1683 for s in self.dependencies: | 1505 for s in self.dependencies: |
1684 work_queue.enqueue(s) | 1506 work_queue.enqueue(s) |
1685 work_queue.flush({}, None, [], options=self._options) | 1507 work_queue.flush({}, None, [], options=self._options) |
1686 | 1508 |
1687 def GetURLAndRev(dep): | 1509 def GetURLAndRev(dep): |
1688 """Returns the revision-qualified SCM url for a Dependency.""" | 1510 """Returns the revision-qualified SCM url for a Dependency.""" |
1689 if dep.parsed_url is None: | 1511 if dep.parsed_url is None: |
1690 return None | 1512 return None |
1691 if isinstance(dep.parsed_url, self.FileImpl): | 1513 url, _ = gclient_utils.SplitUrlRevision(dep.parsed_url) |
1692 original_url = dep.parsed_url.file_location | |
1693 else: | |
1694 original_url = dep.parsed_url | |
1695 url, _ = gclient_utils.SplitUrlRevision(original_url) | |
1696 scm = gclient_scm.CreateSCM( | 1514 scm = gclient_scm.CreateSCM( |
1697 original_url, self.root_dir, dep.name, self.outbuf) | 1515 dep.parsed_url, self.root_dir, dep.name, self.outbuf) |
1698 if not os.path.isdir(scm.checkout_path): | 1516 if not os.path.isdir(scm.checkout_path): |
1699 return None | 1517 return None |
1700 return '%s@%s' % (url, scm.revinfo(self._options, [], None)) | 1518 return '%s@%s' % (url, scm.revinfo(self._options, [], None)) |
1701 | 1519 |
1702 if self._options.snapshot: | 1520 if self._options.snapshot: |
1703 new_gclient = '' | 1521 new_gclient = '' |
1704 # First level at .gclient | 1522 # First level at .gclient |
1705 for d in self.dependencies: | 1523 for d in self.dependencies: |
1706 entries = {} | 1524 entries = {} |
1707 def GrabDeps(dep): | 1525 def GrabDeps(dep): |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1771 | 1589 |
1772 @property | 1590 @property |
1773 def target_os(self): | 1591 def target_os(self): |
1774 return self._enforced_os | 1592 return self._enforced_os |
1775 | 1593 |
1776 | 1594 |
1777 #### gclient commands. | 1595 #### gclient commands. |
1778 | 1596 |
1779 | 1597 |
1780 def CMDcleanup(parser, args): | 1598 def CMDcleanup(parser, args): |
1781 """Cleans up all working copies. | 1599 """DEPRECATED: SVN-only. Cleaned up all working copies. |
1782 | 1600 |
1783 Mostly svn-specific. Simply runs 'svn cleanup' for each module. | 1601 This is a no-op in Git. |
1784 """ | 1602 """ |
1785 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', | 1603 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
1786 help='override deps for the specified (comma-separated) ' | 1604 help='override deps for the specified (comma-separated) ' |
1787 'platform(s); \'all\' will process all deps_os ' | 1605 'platform(s); \'all\' will process all deps_os ' |
1788 'references') | 1606 'references') |
1789 (options, args) = parser.parse_args(args) | 1607 (options, args) = parser.parse_args(args) |
1790 client = GClient.LoadCurrentConfig(options) | 1608 client = GClient.LoadCurrentConfig(options) |
1791 if not client: | 1609 if not client: |
1792 raise gclient_utils.Error('client not configured; see \'gclient config\'') | 1610 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
1793 if options.verbose: | 1611 if options.verbose: |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1956 return 0 | 1774 return 0 |
1957 | 1775 |
1958 | 1776 |
1959 @subcommand.epilog("""Example: | 1777 @subcommand.epilog("""Example: |
1960 gclient pack > patch.txt | 1778 gclient pack > patch.txt |
1961 generate simple patch for configured client and dependences | 1779 generate simple patch for configured client and dependences |
1962 """) | 1780 """) |
1963 def CMDpack(parser, args): | 1781 def CMDpack(parser, args): |
1964 """Generates a patch which can be applied at the root of the tree. | 1782 """Generates a patch which can be applied at the root of the tree. |
1965 | 1783 |
1966 Internally, runs 'svn diff'/'git diff' on each checked out module and | 1784 Internally, runs 'git diff' on each checked out module and |
1967 dependencies, and performs minimal postprocessing of the output. The | 1785 dependencies, and performs minimal postprocessing of the output. The |
1968 resulting patch is printed to stdout and can be applied to a freshly | 1786 resulting patch is printed to stdout and can be applied to a freshly |
1969 checked out tree via 'patch -p0 < patchfile'. | 1787 checked out tree via 'patch -p0 < patchfile'. |
1970 """ | 1788 """ |
1971 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', | 1789 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
1972 help='override deps for the specified (comma-separated) ' | 1790 help='override deps for the specified (comma-separated) ' |
1973 'platform(s); \'all\' will process all deps_os ' | 1791 'platform(s); \'all\' will process all deps_os ' |
1974 'references') | 1792 'references') |
1975 parser.remove_option('--jobs') | 1793 parser.remove_option('--jobs') |
1976 (options, args) = parser.parse_args(args) | 1794 (options, args) = parser.parse_args(args) |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2011 | 1829 |
2012 JSON output format: | 1830 JSON output format: |
2013 If the --output-json option is specified, the following document structure will | 1831 If the --output-json option is specified, the following document structure will |
2014 be emitted to the provided file. 'null' entries may occur for subprojects which | 1832 be emitted to the provided file. 'null' entries may occur for subprojects which |
2015 are present in the gclient solution, but were not processed (due to custom_deps, | 1833 are present in the gclient solution, but were not processed (due to custom_deps, |
2016 os_deps, etc.) | 1834 os_deps, etc.) |
2017 | 1835 |
2018 { | 1836 { |
2019 "solutions" : { | 1837 "solutions" : { |
2020 "<name>": { # <name> is the posix-normalized path to the solution. | 1838 "<name>": { # <name> is the posix-normalized path to the solution. |
2021 "revision": [<svn rev int>|<git id hex string>|null], | 1839 "revision": [<git id hex string>|null], |
2022 "scm": ["svn"|"git"|null], | 1840 "scm": ["git"|null], |
2023 } | 1841 } |
2024 } | 1842 } |
2025 } | 1843 } |
2026 """) | 1844 """) |
2027 def CMDsync(parser, args): | 1845 def CMDsync(parser, args): |
2028 """Checkout/update all modules.""" | 1846 """Checkout/update all modules.""" |
2029 parser.add_option('-f', '--force', action='store_true', | 1847 parser.add_option('-f', '--force', action='store_true', |
2030 help='force update even for unchanged modules') | 1848 help='force update even for unchanged modules') |
2031 parser.add_option('-n', '--nohooks', action='store_true', | 1849 parser.add_option('-n', '--nohooks', action='store_true', |
2032 help='don\'t run hooks after the update is complete') | 1850 help='don\'t run hooks after the update is complete') |
2033 parser.add_option('-p', '--noprehooks', action='store_true', | 1851 parser.add_option('-p', '--noprehooks', action='store_true', |
2034 help='don\'t run pre-DEPS hooks', default=False) | 1852 help='don\'t run pre-DEPS hooks', default=False) |
2035 parser.add_option('-r', '--revision', action='append', | 1853 parser.add_option('-r', '--revision', action='append', |
2036 dest='revisions', metavar='REV', default=[], | 1854 dest='revisions', metavar='REV', default=[], |
2037 help='Enforces revision/hash for the solutions with the ' | 1855 help='Enforces revision/hash for the solutions with the ' |
2038 'format src@rev. The src@ part is optional and can be ' | 1856 'format src@rev. The src@ part is optional and can be ' |
2039 'skipped. -r can be used multiple times when .gclient ' | 1857 'skipped. -r can be used multiple times when .gclient ' |
2040 'has multiple solutions configured and will work even ' | 1858 'has multiple solutions configured and will work even ' |
2041 'if the src@ part is skipped. Note that specifying ' | 1859 'if the src@ part is skipped. Note that specifying ' |
2042 '--revision means your safesync_url gets ignored.') | 1860 '--revision means your safesync_url gets ignored.') |
2043 parser.add_option('--with_branch_heads', action='store_true', | 1861 parser.add_option('--with_branch_heads', action='store_true', |
2044 help='Clone git "branch_heads" refspecs in addition to ' | 1862 help='Clone git "branch_heads" refspecs in addition to ' |
2045 'the default refspecs. This adds about 1/2GB to a ' | 1863 'the default refspecs. This adds about 1/2GB to a ' |
2046 'full checkout. (git only)') | 1864 'full checkout. (git only)') |
2047 parser.add_option('--with_tags', action='store_true', | 1865 parser.add_option('--with_tags', action='store_true', |
2048 help='Clone git tags in addition to the default refspecs.') | 1866 help='Clone git tags in addition to the default refspecs.') |
2049 parser.add_option('-t', '--transitive', action='store_true', | |
2050 help='When a revision is specified (in the DEPS file or ' | |
2051 'with the command-line flag), transitively update ' | |
2052 'the dependencies to the date of the given revision. ' | |
2053 'Only supported for SVN repositories.') | |
2054 parser.add_option('-H', '--head', action='store_true', | 1867 parser.add_option('-H', '--head', action='store_true', |
2055 help='skips any safesync_urls specified in ' | 1868 help='skips any safesync_urls specified in ' |
2056 'configured solutions and sync to head instead') | 1869 'configured solutions and sync to head instead') |
2057 parser.add_option('-D', '--delete_unversioned_trees', action='store_true', | 1870 parser.add_option('-D', '--delete_unversioned_trees', action='store_true', |
2058 help='Deletes from the working copy any dependencies that ' | 1871 help='Deletes from the working copy any dependencies that ' |
2059 'have been removed since the last sync, as long as ' | 1872 'have been removed since the last sync, as long as ' |
2060 'there are no local modifications. When used with ' | 1873 'there are no local modifications. When used with ' |
2061 '--force, such dependencies are removed even if they ' | 1874 '--force, such dependencies are removed even if they ' |
2062 'have local modifications. When used with --reset, ' | 1875 'have local modifications. When used with --reset, ' |
2063 'all untracked directories are removed from the ' | 1876 'all untracked directories are removed from the ' |
2064 'working copy, excluding those which are explicitly ' | 1877 'working copy, excluding those which are explicitly ' |
2065 'ignored in the repository.') | 1878 'ignored in the repository.') |
2066 parser.add_option('-R', '--reset', action='store_true', | 1879 parser.add_option('-R', '--reset', action='store_true', |
2067 help='resets any local changes before updating (git only)') | 1880 help='resets any local changes before updating (git only)') |
2068 parser.add_option('-M', '--merge', action='store_true', | 1881 parser.add_option('-M', '--merge', action='store_true', |
2069 help='merge upstream changes instead of trying to ' | 1882 help='merge upstream changes instead of trying to ' |
2070 'fast-forward or rebase') | 1883 'fast-forward or rebase') |
2071 parser.add_option('-A', '--auto_rebase', action='store_true', | 1884 parser.add_option('-A', '--auto_rebase', action='store_true', |
2072 help='Automatically rebase repositories against local ' | 1885 help='Automatically rebase repositories against local ' |
2073 'checkout during update (git only).') | 1886 'checkout during update (git only).') |
2074 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', | 1887 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
2075 help='override deps for the specified (comma-separated) ' | 1888 help='override deps for the specified (comma-separated) ' |
2076 'platform(s); \'all\' will process all deps_os ' | 1889 'platform(s); \'all\' will process all deps_os ' |
2077 'references') | 1890 'references') |
2078 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true', | |
2079 help='Skip svn up whenever possible by requesting ' | |
2080 'actual HEAD revision from the repository') | |
2081 parser.add_option('--upstream', action='store_true', | 1891 parser.add_option('--upstream', action='store_true', |
2082 help='Make repo state match upstream branch.') | 1892 help='Make repo state match upstream branch.') |
2083 parser.add_option('--output-json', | 1893 parser.add_option('--output-json', |
2084 help='Output a json document to this path containing ' | 1894 help='Output a json document to this path containing ' |
2085 'summary information about the sync.') | 1895 'summary information about the sync.') |
2086 parser.add_option('--no-history', action='store_true', | 1896 parser.add_option('--no-history', action='store_true', |
2087 help='GIT ONLY - Reduces the size/time of the checkout at ' | 1897 help='GIT ONLY - Reduces the size/time of the checkout at ' |
2088 'the cost of no history. Requires Git 1.9+') | 1898 'the cost of no history. Requires Git 1.9+') |
2089 parser.add_option('--shallow', action='store_true', | 1899 parser.add_option('--shallow', action='store_true', |
2090 help='GIT ONLY - Do a shallow clone into the cache dir. ' | 1900 help='GIT ONLY - Do a shallow clone into the cache dir. ' |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2144 raise gclient_utils.Error('client not configured; see \'gclient config\'') | 1954 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
2145 if options.verbose: | 1955 if options.verbose: |
2146 client.PrintLocationAndContents() | 1956 client.PrintLocationAndContents() |
2147 return client.RunOnDeps('diff', args) | 1957 return client.RunOnDeps('diff', args) |
2148 | 1958 |
2149 | 1959 |
2150 def CMDrevert(parser, args): | 1960 def CMDrevert(parser, args): |
2151 """Reverts all modifications in every dependencies. | 1961 """Reverts all modifications in every dependencies. |
2152 | 1962 |
2153 That's the nuclear option to get back to a 'clean' state. It removes anything | 1963 That's the nuclear option to get back to a 'clean' state. It removes anything |
2154 that shows up in svn status.""" | 1964 that shows up in git status.""" |
2155 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', | 1965 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
2156 help='override deps for the specified (comma-separated) ' | 1966 help='override deps for the specified (comma-separated) ' |
2157 'platform(s); \'all\' will process all deps_os ' | 1967 'platform(s); \'all\' will process all deps_os ' |
2158 'references') | 1968 'references') |
2159 parser.add_option('-n', '--nohooks', action='store_true', | 1969 parser.add_option('-n', '--nohooks', action='store_true', |
2160 help='don\'t run hooks after the revert is complete') | 1970 help='don\'t run hooks after the revert is complete') |
2161 parser.add_option('-p', '--noprehooks', action='store_true', | 1971 parser.add_option('-p', '--noprehooks', action='store_true', |
2162 help='don\'t run pre-DEPS hooks', default=False) | 1972 help='don\'t run pre-DEPS hooks', default=False) |
2163 parser.add_option('--upstream', action='store_true', | 1973 parser.add_option('--upstream', action='store_true', |
2164 help='Make repo state match upstream branch.') | 1974 help='Make repo state match upstream branch.') |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2196 options.force = True | 2006 options.force = True |
2197 options.nohooks = False | 2007 options.nohooks = False |
2198 return client.RunOnDeps('runhooks', args) | 2008 return client.RunOnDeps('runhooks', args) |
2199 | 2009 |
2200 | 2010 |
2201 def CMDrevinfo(parser, args): | 2011 def CMDrevinfo(parser, args): |
2202 """Outputs revision info mapping for the client and its dependencies. | 2012 """Outputs revision info mapping for the client and its dependencies. |
2203 | 2013 |
2204 This allows the capture of an overall 'revision' for the source tree that | 2014 This allows the capture of an overall 'revision' for the source tree that |
2205 can be used to reproduce the same tree in the future. It is only useful for | 2015 can be used to reproduce the same tree in the future. It is only useful for |
2206 'unpinned dependencies', i.e. DEPS/deps references without a svn revision | 2016 'unpinned dependencies', i.e. DEPS/deps references without a git hash. |
2207 number or a git hash. A git branch name isn't 'pinned' since the actual | 2017 A git branch name isn't 'pinned' since the actual commit can change. |
2208 commit can change. | |
2209 """ | 2018 """ |
2210 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', | 2019 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
2211 help='override deps for the specified (comma-separated) ' | 2020 help='override deps for the specified (comma-separated) ' |
2212 'platform(s); \'all\' will process all deps_os ' | 2021 'platform(s); \'all\' will process all deps_os ' |
2213 'references') | 2022 'references') |
2214 parser.add_option('-a', '--actual', action='store_true', | 2023 parser.add_option('-a', '--actual', action='store_true', |
2215 help='gets the actual checked out revisions instead of the ' | 2024 help='gets the actual checked out revisions instead of the ' |
2216 'ones specified in the DEPS and .gclient files') | 2025 'ones specified in the DEPS and .gclient files') |
2217 parser.add_option('-s', '--snapshot', action='store_true', | 2026 parser.add_option('-s', '--snapshot', action='store_true', |
2218 help='creates a snapshot .gclient file of the current ' | 2027 help='creates a snapshot .gclient file of the current ' |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2264 | 2073 |
2265 def __init__(self, **kwargs): | 2074 def __init__(self, **kwargs): |
2266 optparse.OptionParser.__init__( | 2075 optparse.OptionParser.__init__( |
2267 self, version='%prog ' + __version__, **kwargs) | 2076 self, version='%prog ' + __version__, **kwargs) |
2268 | 2077 |
2269 # Some arm boards have issues with parallel sync. | 2078 # Some arm boards have issues with parallel sync. |
2270 if platform.machine().startswith('arm'): | 2079 if platform.machine().startswith('arm'): |
2271 jobs = 1 | 2080 jobs = 1 |
2272 else: | 2081 else: |
2273 jobs = max(8, gclient_utils.NumLocalCpus()) | 2082 jobs = max(8, gclient_utils.NumLocalCpus()) |
2274 # cmp: 2013/06/19 | |
2275 # Temporary workaround to lower bot-load on SVN server. | |
2276 # Bypassed if a bot_update flag is detected. | |
2277 if (os.environ.get('CHROME_HEADLESS') == '1' and | |
2278 not os.path.exists('update.flag')): | |
2279 jobs = 1 | |
2280 | 2083 |
2281 self.add_option( | 2084 self.add_option( |
2282 '-j', '--jobs', default=jobs, type='int', | 2085 '-j', '--jobs', default=jobs, type='int', |
2283 help='Specify how many SCM commands can run in parallel; defaults to ' | 2086 help='Specify how many SCM commands can run in parallel; defaults to ' |
2284 '%default on this machine') | 2087 '%default on this machine') |
2285 self.add_option( | 2088 self.add_option( |
2286 '-v', '--verbose', action='count', default=0, | 2089 '-v', '--verbose', action='count', default=0, |
2287 help='Produces additional output for diagnostics. Can be used up to ' | 2090 help='Produces additional output for diagnostics. Can be used up to ' |
2288 'three times for more logging info.') | 2091 'three times for more logging info.') |
2289 self.add_option( | 2092 self.add_option( |
(...skipping 30 matching lines...) Expand all Loading... |
2320 # GClient.RunOnDeps expects it even if not applicable. | 2123 # GClient.RunOnDeps expects it even if not applicable. |
2321 options.revisions = [] | 2124 options.revisions = [] |
2322 if not hasattr(options, 'head'): | 2125 if not hasattr(options, 'head'): |
2323 options.head = None | 2126 options.head = None |
2324 if not hasattr(options, 'nohooks'): | 2127 if not hasattr(options, 'nohooks'): |
2325 options.nohooks = True | 2128 options.nohooks = True |
2326 if not hasattr(options, 'noprehooks'): | 2129 if not hasattr(options, 'noprehooks'): |
2327 options.noprehooks = True | 2130 options.noprehooks = True |
2328 if not hasattr(options, 'deps_os'): | 2131 if not hasattr(options, 'deps_os'): |
2329 options.deps_os = None | 2132 options.deps_os = None |
2330 if not hasattr(options, 'manually_grab_svn_rev'): | |
2331 options.manually_grab_svn_rev = None | |
2332 if not hasattr(options, 'force'): | 2133 if not hasattr(options, 'force'): |
2333 options.force = None | 2134 options.force = None |
2334 return (options, args) | 2135 return (options, args) |
2335 | 2136 |
2336 | 2137 |
2337 def disable_buffering(): | 2138 def disable_buffering(): |
2338 # Make stdout auto-flush so buildbot doesn't kill us during lengthy | 2139 # Make stdout auto-flush so buildbot doesn't kill us during lengthy |
2339 # operations. Python as a strong tendency to buffer sys.stdout. | 2140 # operations. Python as a strong tendency to buffer sys.stdout. |
2340 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout) | 2141 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout) |
2341 # Make stdout annotated with the thread ids. | 2142 # Make stdout annotated with the thread ids. |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2374 | 2175 |
2375 | 2176 |
2376 if '__main__' == __name__: | 2177 if '__main__' == __name__: |
2377 try: | 2178 try: |
2378 sys.exit(main(sys.argv[1:])) | 2179 sys.exit(main(sys.argv[1:])) |
2379 except KeyboardInterrupt: | 2180 except KeyboardInterrupt: |
2380 sys.stderr.write('interrupted\n') | 2181 sys.stderr.write('interrupted\n') |
2381 sys.exit(1) | 2182 sys.exit(1) |
2382 | 2183 |
2383 # vim: ts=2:sw=2:tw=80:et: | 2184 # vim: ts=2:sw=2:tw=80:et: |
OLD | NEW |