OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 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.5' | 9 __version__ = '1.6' |
10 | 10 |
11 # TODO(joi) Add caching where appropriate/needed. The API is designed to allow | 11 # TODO(joi) Add caching where appropriate/needed. The API is designed to allow |
12 # caching (between all different invocations of presubmit scripts for a given | 12 # caching (between all different invocations of presubmit scripts for a given |
13 # change). We should add it as our presubmit scripts start feeling slow. | 13 # change). We should add it as our presubmit scripts start feeling slow. |
14 | 14 |
15 import cPickle # Exposed through the API. | 15 import cPickle # Exposed through the API. |
16 import cStringIO # Exposed through the API. | 16 import cStringIO # Exposed through the API. |
17 import exceptions | |
18 import fnmatch | 17 import fnmatch |
19 import glob | 18 import glob |
20 import logging | 19 import logging |
21 import marshal # Exposed through the API. | 20 import marshal # Exposed through the API. |
22 import optparse | 21 import optparse |
23 import os # Somewhat exposed through the API. | 22 import os # Somewhat exposed through the API. |
24 import pickle # Exposed through the API. | 23 import pickle # Exposed through the API. |
25 import random | 24 import random |
26 import re # Exposed through the API. | 25 import re # Exposed through the API. |
27 import sys # Parts exposed through API. | 26 import sys # Parts exposed through API. |
(...skipping 21 matching lines...) Expand all Loading... |
49 import owners | 48 import owners |
50 import presubmit_canned_checks | 49 import presubmit_canned_checks |
51 import scm | 50 import scm |
52 import subprocess2 as subprocess # Exposed through the API. | 51 import subprocess2 as subprocess # Exposed through the API. |
53 | 52 |
54 | 53 |
55 # Ask for feedback only once in program lifetime. | 54 # Ask for feedback only once in program lifetime. |
56 _ASKED_FOR_FEEDBACK = False | 55 _ASKED_FOR_FEEDBACK = False |
57 | 56 |
58 | 57 |
59 class NotImplementedException(Exception): | 58 class PresubmitFailure(Exception): |
60 """We're leaving placeholders in a bunch of places to remind us of the | |
61 design of the API, but we have not implemented all of it yet. Implement as | |
62 the need arises. | |
63 """ | |
64 pass | 59 pass |
65 | 60 |
66 | 61 |
67 def normpath(path): | 62 def normpath(path): |
68 '''Version of os.path.normpath that also changes backward slashes to | 63 '''Version of os.path.normpath that also changes backward slashes to |
69 forward slashes when not running on Windows. | 64 forward slashes when not running on Windows. |
70 ''' | 65 ''' |
71 # This is safe to always do because the Windows version of os.path.normpath | 66 # This is safe to always do because the Windows version of os.path.normpath |
72 # will replace forward slashes with backward slashes. | 67 # will replace forward slashes with backward slashes. |
73 path = path.replace(os.sep, '/') | 68 path = path.replace(os.sep, '/') |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
171 should_prompt = True | 166 should_prompt = True |
172 | 167 |
173 class PresubmitNotifyResult(PresubmitResult): | 168 class PresubmitNotifyResult(PresubmitResult): |
174 """Just print something to the screen -- but it's not even a warning.""" | 169 """Just print something to the screen -- but it's not even a warning.""" |
175 pass | 170 pass |
176 | 171 |
177 class MailTextResult(PresubmitResult): | 172 class MailTextResult(PresubmitResult): |
178 """A warning that should be included in the review request email.""" | 173 """A warning that should be included in the review request email.""" |
179 def __init__(self, *args, **kwargs): | 174 def __init__(self, *args, **kwargs): |
180 super(OutputApi.MailTextResult, self).__init__() | 175 super(OutputApi.MailTextResult, self).__init__() |
181 raise NotImplementedException() | 176 raise NotImplementedError() |
182 | 177 |
183 | 178 |
184 class InputApi(object): | 179 class InputApi(object): |
185 """An instance of this object is passed to presubmit scripts so they can | 180 """An instance of this object is passed to presubmit scripts so they can |
186 know stuff about the change they're looking at. | 181 know stuff about the change they're looking at. |
187 """ | 182 """ |
188 # Method could be a function | 183 # Method could be a function |
189 # pylint: disable=R0201 | 184 # pylint: disable=R0201 |
190 | 185 |
191 # File extensions that are considered source files from a style guide | 186 # File extensions that are considered source files from a style guide |
(...skipping 23 matching lines...) Expand all Loading... |
215 r".*\bsconsbuild[\\\/].*", | 210 r".*\bsconsbuild[\\\/].*", |
216 # All caps files like README and LICENCE. | 211 # All caps files like README and LICENCE. |
217 r".*\b[A-Z0-9_]{2,}$", | 212 r".*\b[A-Z0-9_]{2,}$", |
218 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) | 213 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) |
219 r"(|.*[\\\/])\.git[\\\/].*", | 214 r"(|.*[\\\/])\.git[\\\/].*", |
220 r"(|.*[\\\/])\.svn[\\\/].*", | 215 r"(|.*[\\\/])\.svn[\\\/].*", |
221 ) | 216 ) |
222 | 217 |
223 # TODO(dpranke): Update callers to pass in tbr, host_url, remove | 218 # TODO(dpranke): Update callers to pass in tbr, host_url, remove |
224 # default arguments. | 219 # default arguments. |
225 def __init__(self, change, presubmit_path, is_committing, tbr, host_url=None): | 220 def __init__(self, change, presubmit_path, is_committing, tbr, host_url, |
| 221 verbose): |
226 """Builds an InputApi object. | 222 """Builds an InputApi object. |
227 | 223 |
228 Args: | 224 Args: |
229 change: A presubmit.Change object. | 225 change: A presubmit.Change object. |
230 presubmit_path: The path to the presubmit script being processed. | 226 presubmit_path: The path to the presubmit script being processed. |
231 is_committing: True if the change is about to be committed. | 227 is_committing: True if the change is about to be committed. |
232 tbr: True if '--tbr' was passed to skip any reviewer/owner checks | 228 tbr: True if '--tbr' was passed to skip any reviewer/owner checks |
233 host_url: scheme, host, and path of rietveld instance | 229 host_url: scheme, host, and path of rietveld instance |
234 """ | 230 """ |
235 # Version number of the presubmit_support script. | 231 # Version number of the presubmit_support script. |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
269 # The local path of the currently-being-processed presubmit script. | 265 # The local path of the currently-being-processed presubmit script. |
270 self._current_presubmit_path = os.path.dirname(presubmit_path) | 266 self._current_presubmit_path = os.path.dirname(presubmit_path) |
271 | 267 |
272 # We carry the canned checks so presubmit scripts can easily use them. | 268 # We carry the canned checks so presubmit scripts can easily use them. |
273 self.canned_checks = presubmit_canned_checks | 269 self.canned_checks = presubmit_canned_checks |
274 | 270 |
275 # TODO(dpranke): figure out a list of all approved owners for a repo | 271 # TODO(dpranke): figure out a list of all approved owners for a repo |
276 # in order to be able to handle wildcard OWNERS files? | 272 # in order to be able to handle wildcard OWNERS files? |
277 self.owners_db = owners.Database(change.RepositoryRoot(), | 273 self.owners_db = owners.Database(change.RepositoryRoot(), |
278 fopen=file, os_path=self.os_path) | 274 fopen=file, os_path=self.os_path) |
| 275 self.verbose = verbose |
279 | 276 |
280 def PresubmitLocalPath(self): | 277 def PresubmitLocalPath(self): |
281 """Returns the local path of the presubmit script currently being run. | 278 """Returns the local path of the presubmit script currently being run. |
282 | 279 |
283 This is useful if you don't want to hard-code absolute paths in the | 280 This is useful if you don't want to hard-code absolute paths in the |
284 presubmit script. For example, It can be used to find another file | 281 presubmit script. For example, It can be used to find another file |
285 relative to the PRESUBMIT.py script, so the whole tree can be branched and | 282 relative to the PRESUBMIT.py script, so the whole tree can be branched and |
286 the presubmit script still works, without editing its content. | 283 the presubmit script still works, without editing its content. |
287 """ | 284 """ |
288 return self._current_presubmit_path | 285 return self._current_presubmit_path |
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
588 # Method 'NNN' is abstract in class 'NNN' but is not overridden | 585 # Method 'NNN' is abstract in class 'NNN' but is not overridden |
589 # pylint: disable=W0223 | 586 # pylint: disable=W0223 |
590 | 587 |
591 def __init__(self, *args, **kwargs): | 588 def __init__(self, *args, **kwargs): |
592 AffectedFile.__init__(self, *args, **kwargs) | 589 AffectedFile.__init__(self, *args, **kwargs) |
593 self._server_path = None | 590 self._server_path = None |
594 self._is_text_file = None | 591 self._is_text_file = None |
595 | 592 |
596 def ServerPath(self): | 593 def ServerPath(self): |
597 if self._server_path is None: | 594 if self._server_path is None: |
598 raise NotImplementedException() # TODO(maruel) Implement. | 595 raise NotImplementedError('TODO(maruel) Implement.') |
599 return self._server_path | 596 return self._server_path |
600 | 597 |
601 def IsDirectory(self): | 598 def IsDirectory(self): |
602 if self._is_directory is None: | 599 if self._is_directory is None: |
603 path = self.AbsoluteLocalPath() | 600 path = self.AbsoluteLocalPath() |
604 if os.path.exists(path): | 601 if os.path.exists(path): |
605 # Retrieve directly from the file system; it is much faster than | 602 # Retrieve directly from the file system; it is much faster than |
606 # querying subversion, especially on Windows. | 603 # querying subversion, especially on Windows. |
607 self._is_directory = os.path.isdir(path) | 604 self._is_directory = os.path.isdir(path) |
608 else: | 605 else: |
609 # raise NotImplementedException() # TODO(maruel) Implement. | |
610 self._is_directory = False | 606 self._is_directory = False |
611 return self._is_directory | 607 return self._is_directory |
612 | 608 |
613 def Property(self, property_name): | 609 def Property(self, property_name): |
614 if not property_name in self._properties: | 610 if not property_name in self._properties: |
615 raise NotImplementedException() # TODO(maruel) Implement. | 611 raise NotImplementedError('TODO(maruel) Implement.') |
616 return self._properties[property_name] | 612 return self._properties[property_name] |
617 | 613 |
618 def IsTextFile(self): | 614 def IsTextFile(self): |
619 if self._is_text_file is None: | 615 if self._is_text_file is None: |
620 if self.Action() == 'D': | 616 if self.Action() == 'D': |
621 # A deleted file is not a text file. | 617 # A deleted file is not a text file. |
622 self._is_text_file = False | 618 self._is_text_file = False |
623 elif self.IsDirectory(): | 619 elif self.IsDirectory(): |
624 self._is_text_file = False | 620 self._is_text_file = False |
625 else: | 621 else: |
626 # raise NotImplementedException() # TODO(maruel) Implement. | |
627 self._is_text_file = os.path.isfile(self.AbsoluteLocalPath()) | 622 self._is_text_file = os.path.isfile(self.AbsoluteLocalPath()) |
628 return self._is_text_file | 623 return self._is_text_file |
629 | 624 |
630 def GenerateScmDiff(self): | 625 def GenerateScmDiff(self): |
631 return scm.GIT.GenerateDiff(self._local_root, files=[self.LocalPath(),]) | 626 return scm.GIT.GenerateDiff(self._local_root, files=[self.LocalPath(),]) |
632 | 627 |
633 class Change(object): | 628 class Change(object): |
634 """Describe a change. | 629 """Describe a change. |
635 | 630 |
636 Used directly by the presubmit scripts to query the current change being | 631 Used directly by the presubmit scripts to query the current change being |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
854 p = os.path.join(directory, 'PRESUBMIT.py') | 849 p = os.path.join(directory, 'PRESUBMIT.py') |
855 if os.path.isfile(p): | 850 if os.path.isfile(p): |
856 results.append(p) | 851 results.append(p) |
857 | 852 |
858 logging.debug('Presubmit files: %s' % ','.join(results)) | 853 logging.debug('Presubmit files: %s' % ','.join(results)) |
859 return results | 854 return results |
860 | 855 |
861 | 856 |
862 class GetTrySlavesExecuter(object): | 857 class GetTrySlavesExecuter(object): |
863 @staticmethod | 858 @staticmethod |
864 def ExecPresubmitScript(script_text): | 859 def ExecPresubmitScript(script_text, presubmit_path): |
865 """Executes GetPreferredTrySlaves() from a single presubmit script. | 860 """Executes GetPreferredTrySlaves() from a single presubmit script. |
866 | 861 |
867 Args: | 862 Args: |
868 script_text: The text of the presubmit script. | 863 script_text: The text of the presubmit script. |
869 | 864 |
870 Return: | 865 Return: |
871 A list of try slaves. | 866 A list of try slaves. |
872 """ | 867 """ |
873 context = {} | 868 context = {} |
874 exec script_text in context | 869 try: |
| 870 exec script_text in context |
| 871 except Exception, e: |
| 872 raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e)) |
875 | 873 |
876 function_name = 'GetPreferredTrySlaves' | 874 function_name = 'GetPreferredTrySlaves' |
877 if function_name in context: | 875 if function_name in context: |
878 result = eval(function_name + '()', context) | 876 result = eval(function_name + '()', context) |
879 if not isinstance(result, types.ListType): | 877 if not isinstance(result, types.ListType): |
880 raise exceptions.RuntimeError( | 878 raise PresubmitFailure( |
881 'Presubmit functions must return a list, got a %s instead: %s' % | 879 'Presubmit functions must return a list, got a %s instead: %s' % |
882 (type(result), str(result))) | 880 (type(result), str(result))) |
883 for item in result: | 881 for item in result: |
884 if not isinstance(item, basestring): | 882 if not isinstance(item, basestring): |
885 raise exceptions.RuntimeError('All try slaves names must be strings.') | 883 raise PresubmitFailure('All try slaves names must be strings.') |
886 if item != item.strip(): | 884 if item != item.strip(): |
887 raise exceptions.RuntimeError('Try slave names cannot start/end' | 885 raise PresubmitFailure( |
888 'with whitespace') | 886 'Try slave names cannot start/end with whitespace') |
889 else: | 887 else: |
890 result = [] | 888 result = [] |
891 return result | 889 return result |
892 | 890 |
893 | 891 |
894 def DoGetTrySlaves(changed_files, | 892 def DoGetTrySlaves(changed_files, |
895 repository_root, | 893 repository_root, |
896 default_presubmit, | 894 default_presubmit, |
897 verbose, | 895 verbose, |
898 output_stream): | 896 output_stream): |
(...skipping 10 matching lines...) Expand all Loading... |
909 List of try slaves | 907 List of try slaves |
910 """ | 908 """ |
911 presubmit_files = ListRelevantPresubmitFiles(changed_files, repository_root) | 909 presubmit_files = ListRelevantPresubmitFiles(changed_files, repository_root) |
912 if not presubmit_files and verbose: | 910 if not presubmit_files and verbose: |
913 output_stream.write("Warning, no presubmit.py found.\n") | 911 output_stream.write("Warning, no presubmit.py found.\n") |
914 results = [] | 912 results = [] |
915 executer = GetTrySlavesExecuter() | 913 executer = GetTrySlavesExecuter() |
916 if default_presubmit: | 914 if default_presubmit: |
917 if verbose: | 915 if verbose: |
918 output_stream.write("Running default presubmit script.\n") | 916 output_stream.write("Running default presubmit script.\n") |
919 results += executer.ExecPresubmitScript(default_presubmit) | 917 fake_path = os.path.join(repository_root, 'PRESUBMIT.py') |
| 918 results += executer.ExecPresubmitScript(default_presubmit, fake_path) |
920 for filename in presubmit_files: | 919 for filename in presubmit_files: |
921 filename = os.path.abspath(filename) | 920 filename = os.path.abspath(filename) |
922 if verbose: | 921 if verbose: |
923 output_stream.write("Running %s\n" % filename) | 922 output_stream.write("Running %s\n" % filename) |
924 # Accept CRLF presubmit script. | 923 # Accept CRLF presubmit script. |
925 presubmit_script = gclient_utils.FileRead(filename, 'rU') | 924 presubmit_script = gclient_utils.FileRead(filename, 'rU') |
926 results += executer.ExecPresubmitScript(presubmit_script) | 925 results += executer.ExecPresubmitScript(presubmit_script, filename) |
927 | 926 |
928 slaves = list(set(results)) | 927 slaves = list(set(results)) |
929 if slaves and verbose: | 928 if slaves and verbose: |
930 output_stream.write(', '.join(slaves)) | 929 output_stream.write(', '.join(slaves)) |
931 output_stream.write('\n') | 930 output_stream.write('\n') |
932 return slaves | 931 return slaves |
933 | 932 |
934 | 933 |
935 class PresubmitExecuter(object): | 934 class PresubmitExecuter(object): |
936 def __init__(self, change, committing, tbr, host_url): | 935 def __init__(self, change, committing, tbr, host_url, verbose): |
937 """ | 936 """ |
938 Args: | 937 Args: |
939 change: The Change object. | 938 change: The Change object. |
940 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 939 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
941 tbr: True if '--tbr' was passed to skip any reviewer/owner checks | 940 tbr: True if '--tbr' was passed to skip any reviewer/owner checks |
942 host_url: scheme, host, and path of rietveld instance | 941 host_url: scheme, host, and path of rietveld instance |
943 (or None for default) | 942 (or None for default) |
944 """ | 943 """ |
945 self.change = change | 944 self.change = change |
946 self.committing = committing | 945 self.committing = committing |
947 self.tbr = tbr | 946 self.tbr = tbr |
948 self.host_url = host_url | 947 self.host_url = host_url |
| 948 self.verbose = verbose |
949 | 949 |
950 def ExecPresubmitScript(self, script_text, presubmit_path): | 950 def ExecPresubmitScript(self, script_text, presubmit_path): |
951 """Executes a single presubmit script. | 951 """Executes a single presubmit script. |
952 | 952 |
953 Args: | 953 Args: |
954 script_text: The text of the presubmit script. | 954 script_text: The text of the presubmit script. |
955 presubmit_path: The path to the presubmit file (this will be reported via | 955 presubmit_path: The path to the presubmit file (this will be reported via |
956 input_api.PresubmitLocalPath()). | 956 input_api.PresubmitLocalPath()). |
957 | 957 |
958 Return: | 958 Return: |
959 A list of result objects, empty if no problems. | 959 A list of result objects, empty if no problems. |
960 """ | 960 """ |
961 | 961 |
962 # Change to the presubmit file's directory to support local imports. | 962 # Change to the presubmit file's directory to support local imports. |
963 main_path = os.getcwd() | 963 main_path = os.getcwd() |
964 os.chdir(os.path.dirname(presubmit_path)) | 964 os.chdir(os.path.dirname(presubmit_path)) |
965 | 965 |
966 # Load the presubmit script into context. | 966 # Load the presubmit script into context. |
967 input_api = InputApi(self.change, presubmit_path, self.committing, | 967 input_api = InputApi(self.change, presubmit_path, self.committing, |
968 self.tbr, self.host_url) | 968 self.tbr, self.host_url, self.verbose) |
969 context = {} | 969 context = {} |
970 exec script_text in context | 970 try: |
| 971 exec script_text in context |
| 972 except Exception, e: |
| 973 raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e)) |
971 | 974 |
972 # These function names must change if we make substantial changes to | 975 # These function names must change if we make substantial changes to |
973 # the presubmit API that are not backwards compatible. | 976 # the presubmit API that are not backwards compatible. |
974 if self.committing: | 977 if self.committing: |
975 function_name = 'CheckChangeOnCommit' | 978 function_name = 'CheckChangeOnCommit' |
976 else: | 979 else: |
977 function_name = 'CheckChangeOnUpload' | 980 function_name = 'CheckChangeOnUpload' |
978 if function_name in context: | 981 if function_name in context: |
979 context['__args'] = (input_api, OutputApi()) | 982 context['__args'] = (input_api, OutputApi()) |
980 logging.debug('Running %s in %s' % (function_name, presubmit_path)) | 983 logging.debug('Running %s in %s' % (function_name, presubmit_path)) |
981 result = eval(function_name + '(*__args)', context) | 984 result = eval(function_name + '(*__args)', context) |
982 logging.debug('Running %s done.' % function_name) | 985 logging.debug('Running %s done.' % function_name) |
983 if not (isinstance(result, types.TupleType) or | 986 if not (isinstance(result, types.TupleType) or |
984 isinstance(result, types.ListType)): | 987 isinstance(result, types.ListType)): |
985 raise exceptions.RuntimeError( | 988 raise PresubmitFailure( |
986 'Presubmit functions must return a tuple or list') | 989 'Presubmit functions must return a tuple or list') |
987 for item in result: | 990 for item in result: |
988 if not isinstance(item, OutputApi.PresubmitResult): | 991 if not isinstance(item, OutputApi.PresubmitResult): |
989 raise exceptions.RuntimeError( | 992 raise PresubmitFailure( |
990 'All presubmit results must be of types derived from ' | 993 'All presubmit results must be of types derived from ' |
991 'output_api.PresubmitResult') | 994 'output_api.PresubmitResult') |
992 else: | 995 else: |
993 result = () # no error since the script doesn't care about current event. | 996 result = () # no error since the script doesn't care about current event. |
994 | 997 |
995 # Return the process to the original working directory. | 998 # Return the process to the original working directory. |
996 os.chdir(main_path) | 999 os.chdir(main_path) |
997 return result | 1000 return result |
998 | 1001 |
999 | 1002 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1040 if committing: | 1043 if committing: |
1041 output.write("Running presubmit commit checks ...\n") | 1044 output.write("Running presubmit commit checks ...\n") |
1042 else: | 1045 else: |
1043 output.write("Running presubmit upload checks ...\n") | 1046 output.write("Running presubmit upload checks ...\n") |
1044 start_time = time.time() | 1047 start_time = time.time() |
1045 presubmit_files = ListRelevantPresubmitFiles(change.AbsoluteLocalPaths(True), | 1048 presubmit_files = ListRelevantPresubmitFiles(change.AbsoluteLocalPaths(True), |
1046 change.RepositoryRoot()) | 1049 change.RepositoryRoot()) |
1047 if not presubmit_files and verbose: | 1050 if not presubmit_files and verbose: |
1048 output.write("Warning, no presubmit.py found.\n") | 1051 output.write("Warning, no presubmit.py found.\n") |
1049 results = [] | 1052 results = [] |
1050 executer = PresubmitExecuter(change, committing, tbr, host_url) | 1053 executer = PresubmitExecuter(change, committing, tbr, host_url, verbose) |
1051 if default_presubmit: | 1054 if default_presubmit: |
1052 if verbose: | 1055 if verbose: |
1053 output.write("Running default presubmit script.\n") | 1056 output.write("Running default presubmit script.\n") |
1054 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') | 1057 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') |
1055 results += executer.ExecPresubmitScript(default_presubmit, fake_path) | 1058 results += executer.ExecPresubmitScript(default_presubmit, fake_path) |
1056 for filename in presubmit_files: | 1059 for filename in presubmit_files: |
1057 filename = os.path.abspath(filename) | 1060 filename = os.path.abspath(filename) |
1058 if verbose: | 1061 if verbose: |
1059 output.write("Running %s\n" % filename) | 1062 output.write("Running %s\n" % filename) |
1060 # Accept CRLF presubmit script. | 1063 # Accept CRLF presubmit script. |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1153 | 1156 |
1154 def Main(argv): | 1157 def Main(argv): |
1155 parser = optparse.OptionParser(usage="%prog [options] <files...>", | 1158 parser = optparse.OptionParser(usage="%prog [options] <files...>", |
1156 version="%prog " + str(__version__)) | 1159 version="%prog " + str(__version__)) |
1157 parser.add_option("-c", "--commit", action="store_true", default=False, | 1160 parser.add_option("-c", "--commit", action="store_true", default=False, |
1158 help="Use commit instead of upload checks") | 1161 help="Use commit instead of upload checks") |
1159 parser.add_option("-u", "--upload", action="store_false", dest='commit', | 1162 parser.add_option("-u", "--upload", action="store_false", dest='commit', |
1160 help="Use upload instead of commit checks") | 1163 help="Use upload instead of commit checks") |
1161 parser.add_option("-r", "--recursive", action="store_true", | 1164 parser.add_option("-r", "--recursive", action="store_true", |
1162 help="Act recursively") | 1165 help="Act recursively") |
1163 parser.add_option("-v", "--verbose", action="store_true", default=False, | 1166 parser.add_option("-v", "--verbose", action="count", default=0, |
1164 help="Verbose output") | 1167 help="Use 2 times for more debug info") |
1165 parser.add_option("--name", default='no name') | 1168 parser.add_option("--name", default='no name') |
1166 parser.add_option("--description", default='') | 1169 parser.add_option("--description", default='') |
1167 parser.add_option("--issue", type='int', default=0) | 1170 parser.add_option("--issue", type='int', default=0) |
1168 parser.add_option("--patchset", type='int', default=0) | 1171 parser.add_option("--patchset", type='int', default=0) |
1169 parser.add_option("--root", default=os.getcwd(), | 1172 parser.add_option("--root", default=os.getcwd(), |
1170 help="Search for PRESUBMIT.py up to this directory. " | 1173 help="Search for PRESUBMIT.py up to this directory. " |
1171 "If inherit-review-settings-ok is present in this " | 1174 "If inherit-review-settings-ok is present in this " |
1172 "directory, parent directories up to the root file " | 1175 "directory, parent directories up to the root file " |
1173 "system directories will also be searched.") | 1176 "system directories will also be searched.") |
1174 parser.add_option("--default_presubmit") | 1177 parser.add_option("--default_presubmit") |
1175 parser.add_option("--may_prompt", action='store_true', default=False) | 1178 parser.add_option("--may_prompt", action='store_true', default=False) |
1176 options, args = parser.parse_args(argv) | 1179 options, args = parser.parse_args(argv) |
1177 if options.verbose: | 1180 if options.verbose >= 2: |
1178 logging.basicConfig(level=logging.DEBUG) | 1181 logging.basicConfig(level=logging.DEBUG) |
| 1182 elif options.verbose: |
| 1183 logging.basicConfig(level=logging.INFO) |
| 1184 else: |
| 1185 logging.basicConfig(level=logging.ERROR) |
1179 change_class, files = load_files(options, args) | 1186 change_class, files = load_files(options, args) |
1180 if not change_class: | 1187 if not change_class: |
1181 parser.error('For unversioned directory, <files> is not optional.') | 1188 parser.error('For unversioned directory, <files> is not optional.') |
1182 if options.verbose: | 1189 logging.info('Found %d file(s).' % len(files)) |
1183 print "Found %d file(s)." % len(files) | 1190 try: |
1184 results = DoPresubmitChecks(change_class(options.name, | 1191 results = DoPresubmitChecks( |
1185 options.description, | 1192 change_class(options.name, |
1186 options.root, | 1193 options.description, |
1187 files, | 1194 options.root, |
1188 options.issue, | 1195 files, |
1189 options.patchset), | 1196 options.issue, |
1190 options.commit, | 1197 options.patchset), |
1191 options.verbose, | 1198 options.commit, |
1192 sys.stdout, | 1199 options.verbose, |
1193 sys.stdin, | 1200 sys.stdout, |
1194 options.default_presubmit, | 1201 sys.stdin, |
1195 options.may_prompt) | 1202 options.default_presubmit, |
1196 return not results.should_continue() | 1203 options.may_prompt) |
| 1204 return not results.should_continue() |
| 1205 except PresubmitFailure, e: |
| 1206 print >> sys.stderr, e |
| 1207 print >> sys.stderr, 'Maybe your depot_tools is out of date?' |
| 1208 print >> sys.stderr, 'If all fails, contact maruel@' |
| 1209 return 2 |
1197 | 1210 |
1198 | 1211 |
1199 if __name__ == '__main__': | 1212 if __name__ == '__main__': |
1200 fix_encoding.fix_encoding() | 1213 fix_encoding.fix_encoding() |
1201 sys.exit(Main(None)) | 1214 sys.exit(Main(None)) |
OLD | NEW |