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

Side by Side Diff: presubmit_support.py

Issue 6810012: Add verbose support throught presubmit checks (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: merge error Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « presubmit_canned_checks.py ('k') | tests/presubmit_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/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
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
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
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
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
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
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
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
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
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))
OLDNEW
« no previous file with comments | « presubmit_canned_checks.py ('k') | tests/presubmit_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698