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