OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2006-2009 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 """Enables directory-specific presubmit checks to run at upload and/or commit. | 6 """Enables directory-specific presubmit checks to run at upload and/or commit. |
7 """ | 7 """ |
8 | 8 |
9 __version__ = '1.3.2' | 9 __version__ = '1.3.2' |
10 | 10 |
(...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
382 return "" | 382 return "" |
383 | 383 |
384 def LocalPath(self): | 384 def LocalPath(self): |
385 """Returns the path of this file on the local disk relative to client root. | 385 """Returns the path of this file on the local disk relative to client root. |
386 """ | 386 """ |
387 return normpath(self._path) | 387 return normpath(self._path) |
388 | 388 |
389 def AbsoluteLocalPath(self): | 389 def AbsoluteLocalPath(self): |
390 """Returns the absolute path of this file on the local disk. | 390 """Returns the absolute path of this file on the local disk. |
391 """ | 391 """ |
392 return normpath(os.path.join(self._local_root, self.LocalPath())) | 392 return os.path.abspath(os.path.join(self._local_root, self.LocalPath())) |
393 | 393 |
394 def IsDirectory(self): | 394 def IsDirectory(self): |
395 """Returns true if this object is a directory.""" | 395 """Returns true if this object is a directory.""" |
396 if self._is_directory is None: | 396 if self._is_directory is None: |
397 path = self.AbsoluteLocalPath() | 397 path = self.AbsoluteLocalPath() |
398 self._is_directory = (os.path.exists(path) and | 398 self._is_directory = (os.path.exists(path) and |
399 os.path.isdir(path)) | 399 os.path.isdir(path)) |
400 return self._is_directory | 400 return self._is_directory |
401 | 401 |
402 def Action(self): | 402 def Action(self): |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
555 | 555 |
556 # Matches key/value (or "tag") lines in changelist descriptions. | 556 # Matches key/value (or "tag") lines in changelist descriptions. |
557 _TAG_LINE_RE = re.compile( | 557 _TAG_LINE_RE = re.compile( |
558 '^\s*(?P<key>[A-Z][A-Z_0-9]*)\s*=\s*(?P<value>.*?)\s*$') | 558 '^\s*(?P<key>[A-Z][A-Z_0-9]*)\s*=\s*(?P<value>.*?)\s*$') |
559 | 559 |
560 def __init__(self, name, description, local_root, files, issue, patchset): | 560 def __init__(self, name, description, local_root, files, issue, patchset): |
561 if files is None: | 561 if files is None: |
562 files = [] | 562 files = [] |
563 self._name = name | 563 self._name = name |
564 self._full_description = description | 564 self._full_description = description |
565 self._local_root = local_root | 565 # Convert root into an absolute path. |
| 566 self._local_root = os.path.abspath(local_root) |
566 self.issue = issue | 567 self.issue = issue |
567 self.patchset = patchset | 568 self.patchset = patchset |
568 self.scm = '' | 569 self.scm = '' |
569 | 570 |
570 # From the description text, build up a dictionary of key/value pairs | 571 # From the description text, build up a dictionary of key/value pairs |
571 # plus the description minus all key/value or "tag" lines. | 572 # plus the description minus all key/value or "tag" lines. |
572 self._description_without_tags = [] | 573 self._description_without_tags = [] |
573 self.tags = {} | 574 self.tags = {} |
574 for line in self._full_description.splitlines(): | 575 for line in self._full_description.splitlines(): |
575 m = self._TAG_LINE_RE.match(line) | 576 m = self._TAG_LINE_RE.match(line) |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
757 """Executes a single presubmit script. | 758 """Executes a single presubmit script. |
758 | 759 |
759 Args: | 760 Args: |
760 script_text: The text of the presubmit script. | 761 script_text: The text of the presubmit script. |
761 presubmit_path: The path to the presubmit file (this will be reported via | 762 presubmit_path: The path to the presubmit file (this will be reported via |
762 input_api.PresubmitLocalPath()). | 763 input_api.PresubmitLocalPath()). |
763 | 764 |
764 Return: | 765 Return: |
765 A list of result objects, empty if no problems. | 766 A list of result objects, empty if no problems. |
766 """ | 767 """ |
| 768 |
| 769 # Change to the presubmit file's directory to support local imports. |
| 770 main_path = os.getcwd() |
| 771 os.chdir(os.path.dirname(presubmit_path)) |
| 772 |
| 773 # Load the presubmit script into context. |
767 input_api = InputApi(self.change, presubmit_path, self.committing) | 774 input_api = InputApi(self.change, presubmit_path, self.committing) |
768 context = {} | 775 context = {} |
769 exec script_text in context | 776 exec script_text in context |
770 | 777 |
771 # These function names must change if we make substantial changes to | 778 # These function names must change if we make substantial changes to |
772 # the presubmit API that are not backwards compatible. | 779 # the presubmit API that are not backwards compatible. |
773 if self.committing: | 780 if self.committing: |
774 function_name = 'CheckChangeOnCommit' | 781 function_name = 'CheckChangeOnCommit' |
775 else: | 782 else: |
776 function_name = 'CheckChangeOnUpload' | 783 function_name = 'CheckChangeOnUpload' |
777 if function_name in context: | 784 if function_name in context: |
778 context['__args'] = (input_api, OutputApi()) | 785 context['__args'] = (input_api, OutputApi()) |
779 result = eval(function_name + '(*__args)', context) | 786 result = eval(function_name + '(*__args)', context) |
780 if not (isinstance(result, types.TupleType) or | 787 if not (isinstance(result, types.TupleType) or |
781 isinstance(result, types.ListType)): | 788 isinstance(result, types.ListType)): |
782 raise exceptions.RuntimeError( | 789 raise exceptions.RuntimeError( |
783 'Presubmit functions must return a tuple or list') | 790 'Presubmit functions must return a tuple or list') |
784 for item in result: | 791 for item in result: |
785 if not isinstance(item, OutputApi.PresubmitResult): | 792 if not isinstance(item, OutputApi.PresubmitResult): |
786 raise exceptions.RuntimeError( | 793 raise exceptions.RuntimeError( |
787 'All presubmit results must be of types derived from ' | 794 'All presubmit results must be of types derived from ' |
788 'output_api.PresubmitResult') | 795 'output_api.PresubmitResult') |
789 else: | 796 else: |
790 result = () # no error since the script doesn't care about current event. | 797 result = () # no error since the script doesn't care about current event. |
791 | 798 |
| 799 # Return the process to the original working directory. |
| 800 os.chdir(main_path) |
792 return result | 801 return result |
793 | 802 |
794 | 803 |
795 def DoPresubmitChecks(change, | 804 def DoPresubmitChecks(change, |
796 committing, | 805 committing, |
797 verbose, | 806 verbose, |
798 output_stream, | 807 output_stream, |
799 input_stream, | 808 input_stream, |
800 default_presubmit, | 809 default_presubmit, |
801 may_prompt): | 810 may_prompt): |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
932 options, args = parser.parse_args(argv[1:]) | 941 options, args = parser.parse_args(argv[1:]) |
933 if not options.root: | 942 if not options.root: |
934 options.root = os.getcwd() | 943 options.root = os.getcwd() |
935 if os.path.isdir(os.path.join(options.root, '.git')): | 944 if os.path.isdir(os.path.join(options.root, '.git')): |
936 change_class = GitChange | 945 change_class = GitChange |
937 if not options.files: | 946 if not options.files: |
938 if args: | 947 if args: |
939 options.files = ParseFiles(args, options.recursive) | 948 options.files = ParseFiles(args, options.recursive) |
940 else: | 949 else: |
941 # Grab modified files. | 950 # Grab modified files. |
942 raise NotImplementedException() # TODO(maruel) Implement. | 951 options.files = gclient_scm.CaptureGitStatus([options.root]) |
943 elif os.path.isdir(os.path.join(options.root, '.svn')): | 952 elif os.path.isdir(os.path.join(options.root, '.svn')): |
944 change_class = SvnChange | 953 change_class = SvnChange |
945 if not options.files: | 954 if not options.files: |
946 if args: | 955 if args: |
947 options.files = ParseFiles(args, options.recursive) | 956 options.files = ParseFiles(args, options.recursive) |
948 else: | 957 else: |
949 # Grab modified files. | 958 # Grab modified files. |
950 files = gclient_scm.CaptureSVNStatus([options.root]) | 959 options.files = gclient_scm.CaptureSVNStatus([options.root]) |
951 else: | 960 else: |
952 # Doesn't seem under source control. | 961 # Doesn't seem under source control. |
953 change_class = Change | 962 change_class = Change |
954 if options.verbose: | 963 if options.verbose: |
955 print "Found %d files." % len(options.files) | 964 if len(options.files) != 1: |
| 965 print "Found %d files." % len(options.files) |
| 966 else: |
| 967 print "Found 1 file." |
956 return not DoPresubmitChecks(change_class(options.name, | 968 return not DoPresubmitChecks(change_class(options.name, |
957 options.description, | 969 options.description, |
958 options.root, | 970 options.root, |
959 options.files, | 971 options.files, |
960 options.issue, | 972 options.issue, |
961 options.patchset), | 973 options.patchset), |
962 options.commit, | 974 options.commit, |
963 options.verbose, | 975 options.verbose, |
964 sys.stdout, | 976 sys.stdout, |
965 sys.stdin, | 977 sys.stdin, |
966 options.default_presubmit, | 978 options.default_presubmit, |
967 options.may_prompt) | 979 options.may_prompt) |
968 | 980 |
969 | 981 |
970 if __name__ == '__main__': | 982 if __name__ == '__main__': |
971 sys.exit(Main(sys.argv)) | 983 sys.exit(Main(sys.argv)) |
OLD | NEW |