| OLD | NEW | 
|     1 #!/usr/bin/python |     1 #!/usr/bin/python | 
|     2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |     2 # Copyright (c) 2010 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.5' |     9 __version__ = '1.3.5' | 
|    10  |    10  | 
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|   147     def IsFatal(self): |   147     def IsFatal(self): | 
|   148       """An error that is fatal stops g4 mail/submit immediately, i.e. before |   148       """An error that is fatal stops g4 mail/submit immediately, i.e. before | 
|   149       other presubmit scripts are run. |   149       other presubmit scripts are run. | 
|   150       """ |   150       """ | 
|   151       return False |   151       return False | 
|   152  |   152  | 
|   153     def ShouldPrompt(self): |   153     def ShouldPrompt(self): | 
|   154       """Whether this presubmit result should result in a prompt warning.""" |   154       """Whether this presubmit result should result in a prompt warning.""" | 
|   155       return False |   155       return False | 
|   156  |   156  | 
|   157     def IsMessage(self): |  | 
|   158       """Whether this result contains anything needing to be displayed.""" |  | 
|   159       return True |  | 
|   160  |  | 
|   161   class PresubmitAddText(PresubmitResult): |   157   class PresubmitAddText(PresubmitResult): | 
|   162     """Propagates a line of text back to the caller.""" |   158     """Propagates a line of text back to the caller.""" | 
|   163     def __init__(self, message, items=None, long_text=''): |   159     def __init__(self, message, items=None, long_text=''): | 
|   164       super(OutputApi.PresubmitAddText, self).__init__("ADD: " + message, |   160       super(OutputApi.PresubmitAddText, self).__init__("ADD: " + message, | 
|   165           items, long_text) |   161           items, long_text) | 
|   166  |   162  | 
|   167     def IsMessage(self): |  | 
|   168       return False |  | 
|   169  |  | 
|   170   class PresubmitError(PresubmitResult): |   163   class PresubmitError(PresubmitResult): | 
|   171     """A hard presubmit error.""" |   164     """A hard presubmit error.""" | 
|   172     def IsFatal(self): |   165     def IsFatal(self): | 
|   173       return True |   166       return True | 
|   174  |   167  | 
|   175   class PresubmitPromptWarning(PresubmitResult): |   168   class PresubmitPromptWarning(PresubmitResult): | 
|   176     """An warning that prompts the user if they want to continue.""" |   169     """An warning that prompts the user if they want to continue.""" | 
|   177     def ShouldPrompt(self): |   170     def ShouldPrompt(self): | 
|   178       return True |   171       return True | 
|   179  |   172  | 
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|   222       r".*\bsconsbuild[\\\/].*", |   215       r".*\bsconsbuild[\\\/].*", | 
|   223       # All caps files like README and LICENCE. |   216       # All caps files like README and LICENCE. | 
|   224       r".*\b[A-Z0-9_]{2,}$", |   217       r".*\b[A-Z0-9_]{2,}$", | 
|   225       # SCM (can happen in dual SCM configuration). (Slightly over aggressive) |   218       # SCM (can happen in dual SCM configuration). (Slightly over aggressive) | 
|   226       r"(|.*[\\\/])\.git[\\\/].*", |   219       r"(|.*[\\\/])\.git[\\\/].*", | 
|   227       r"(|.*[\\\/])\.svn[\\\/].*", |   220       r"(|.*[\\\/])\.svn[\\\/].*", | 
|   228   ) |   221   ) | 
|   229  |   222  | 
|   230   # TODO(dpranke): Update callers to pass in tbr, host_url, remove |   223   # TODO(dpranke): Update callers to pass in tbr, host_url, remove | 
|   231   # default arguments. |   224   # default arguments. | 
|   232   def __init__(self, change, presubmit_path, is_committing, tbr=False, |   225   def __init__(self, change, presubmit_path, is_committing, tbr, host_url=None): | 
|   233     host_url='http://codereview.chromium.org'): |  | 
|   234     """Builds an InputApi object. |   226     """Builds an InputApi object. | 
|   235  |   227  | 
|   236     Args: |   228     Args: | 
|   237       change: A presubmit.Change object. |   229       change: A presubmit.Change object. | 
|   238       presubmit_path: The path to the presubmit script being processed. |   230       presubmit_path: The path to the presubmit script being processed. | 
|   239       is_committing: True if the change is about to be committed. |   231       is_committing: True if the change is about to be committed. | 
 |   232       tbr: True if '--tbr' was passed to skip any reviewer/owner checks | 
 |   233       host_url: scheme, host, and path of rietveld instance | 
|   240     """ |   234     """ | 
|   241     # Version number of the presubmit_support script. |   235     # Version number of the presubmit_support script. | 
|   242     self.version = [int(x) for x in __version__.split('.')] |   236     self.version = [int(x) for x in __version__.split('.')] | 
|   243     self.change = change |   237     self.change = change | 
|   244     self.host_url = host_url |   238     self.host_url = host_url | 
|   245     self.is_committing = is_committing |   239     self.is_committing = is_committing | 
|   246     self.tbr = tbr |   240     self.tbr = tbr | 
 |   241     self.host_url = host_url or 'http://codereview.chromium.org' | 
|   247  |   242  | 
|   248     # We expose various modules and functions as attributes of the input_api |   243     # We expose various modules and functions as attributes of the input_api | 
|   249     # so that presubmit scripts don't have to import them. |   244     # so that presubmit scripts don't have to import them. | 
|   250     self.basename = os.path.basename |   245     self.basename = os.path.basename | 
|   251     self.cPickle = cPickle |   246     self.cPickle = cPickle | 
|   252     self.cStringIO = cStringIO |   247     self.cStringIO = cStringIO | 
|   253     self.json = json |   248     self.json = json | 
|   254     self.os_path = os.path |   249     self.os_path = os.path | 
|   255     self.pickle = pickle |   250     self.pickle = pickle | 
|   256     self.marshal = marshal |   251     self.marshal = marshal | 
| (...skipping 671 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|   928     results += executer.ExecPresubmitScript(presubmit_script) |   923     results += executer.ExecPresubmitScript(presubmit_script) | 
|   929  |   924  | 
|   930   slaves = list(set(results)) |   925   slaves = list(set(results)) | 
|   931   if slaves and verbose: |   926   if slaves and verbose: | 
|   932     output_stream.write(', '.join(slaves)) |   927     output_stream.write(', '.join(slaves)) | 
|   933     output_stream.write('\n') |   928     output_stream.write('\n') | 
|   934   return slaves |   929   return slaves | 
|   935  |   930  | 
|   936  |   931  | 
|   937 class PresubmitExecuter(object): |   932 class PresubmitExecuter(object): | 
|   938   def __init__(self, change, committing): |   933   def __init__(self, change, committing, tbr, host_url): | 
|   939     """ |   934     """ | 
|   940     Args: |   935     Args: | 
|   941       change: The Change object. |   936       change: The Change object. | 
|   942       committing: True if 'gcl commit' is running, False if 'gcl upload' is. |   937       committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 
 |   938       tbr: True if '--tbr' was passed to skip any reviewer/owner checks | 
 |   939       host_url: scheme, host, and path of rietveld instance | 
 |   940           (or None for default) | 
|   943     """ |   941     """ | 
|   944     self.change = change |   942     self.change = change | 
|   945     self.committing = committing |   943     self.committing = committing | 
 |   944     self.tbr = tbr | 
 |   945     self.host_url = host_url | 
|   946  |   946  | 
|   947   def ExecPresubmitScript(self, script_text, presubmit_path): |   947   def ExecPresubmitScript(self, script_text, presubmit_path): | 
|   948     """Executes a single presubmit script. |   948     """Executes a single presubmit script. | 
|   949  |   949  | 
|   950     Args: |   950     Args: | 
|   951       script_text: The text of the presubmit script. |   951       script_text: The text of the presubmit script. | 
|   952       presubmit_path: The path to the presubmit file (this will be reported via |   952       presubmit_path: The path to the presubmit file (this will be reported via | 
|   953         input_api.PresubmitLocalPath()). |   953         input_api.PresubmitLocalPath()). | 
|   954  |   954  | 
|   955     Return: |   955     Return: | 
|   956       A list of result objects, empty if no problems. |   956       A list of result objects, empty if no problems. | 
|   957     """ |   957     """ | 
|   958  |   958  | 
|   959     # Change to the presubmit file's directory to support local imports. |   959     # Change to the presubmit file's directory to support local imports. | 
|   960     main_path = os.getcwd() |   960     main_path = os.getcwd() | 
|   961     os.chdir(os.path.dirname(presubmit_path)) |   961     os.chdir(os.path.dirname(presubmit_path)) | 
|   962  |   962  | 
|   963     # Load the presubmit script into context. |   963     # Load the presubmit script into context. | 
|   964     input_api = InputApi(self.change, presubmit_path, self.committing) |   964     input_api = InputApi(self.change, presubmit_path, self.committing, | 
 |   965                          self.tbr, self.host_url) | 
|   965     context = {} |   966     context = {} | 
|   966     exec script_text in context |   967     exec script_text in context | 
|   967  |   968  | 
|   968     # These function names must change if we make substantial changes to |   969     # These function names must change if we make substantial changes to | 
|   969     # the presubmit API that are not backwards compatible. |   970     # the presubmit API that are not backwards compatible. | 
|   970     if self.committing: |   971     if self.committing: | 
|   971       function_name = 'CheckChangeOnCommit' |   972       function_name = 'CheckChangeOnCommit' | 
|   972     else: |   973     else: | 
|   973       function_name = 'CheckChangeOnUpload' |   974       function_name = 'CheckChangeOnUpload' | 
|   974     if function_name in context: |   975     if function_name in context: | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
|   985           raise exceptions.RuntimeError( |   986           raise exceptions.RuntimeError( | 
|   986             'All presubmit results must be of types derived from ' |   987             'All presubmit results must be of types derived from ' | 
|   987             'output_api.PresubmitResult') |   988             'output_api.PresubmitResult') | 
|   988     else: |   989     else: | 
|   989       result = ()  # no error since the script doesn't care about current event. |   990       result = ()  # no error since the script doesn't care about current event. | 
|   990  |   991  | 
|   991     # Return the process to the original working directory. |   992     # Return the process to the original working directory. | 
|   992     os.chdir(main_path) |   993     os.chdir(main_path) | 
|   993     return result |   994     return result | 
|   994  |   995  | 
|   995  |   996 # TODO(dpranke): make all callers pass in tbr, host_url? | 
|   996 def DoPresubmitChecks(change, |   997 def DoPresubmitChecks(change, | 
|   997                       committing, |   998                       committing, | 
|   998                       verbose, |   999                       verbose, | 
|   999                       output_stream, |  1000                       output_stream, | 
|  1000                       input_stream, |  1001                       input_stream, | 
|  1001                       default_presubmit, |  1002                       default_presubmit, | 
|  1002                       may_prompt): |  1003                       may_prompt, | 
 |  1004                       tbr=False, | 
 |  1005                       host_url=None): | 
|  1003   """Runs all presubmit checks that apply to the files in the change. |  1006   """Runs all presubmit checks that apply to the files in the change. | 
|  1004  |  1007  | 
|  1005   This finds all PRESUBMIT.py files in directories enclosing the files in the |  1008   This finds all PRESUBMIT.py files in directories enclosing the files in the | 
|  1006   change (up to the repository root) and calls the relevant entrypoint function |  1009   change (up to the repository root) and calls the relevant entrypoint function | 
|  1007   depending on whether the change is being committed or uploaded. |  1010   depending on whether the change is being committed or uploaded. | 
|  1008  |  1011  | 
|  1009   Prints errors, warnings and notifications.  Prompts the user for warnings |  1012   Prints errors, warnings and notifications.  Prompts the user for warnings | 
|  1010   when needed. |  1013   when needed. | 
|  1011  |  1014  | 
|  1012   Args: |  1015   Args: | 
|  1013     change: The Change object. |  1016     change: The Change object. | 
|  1014     committing: True if 'gcl commit' is running, False if 'gcl upload' is. |  1017     committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 
|  1015     verbose: Prints debug info. |  1018     verbose: Prints debug info. | 
|  1016     output_stream: A stream to write output from presubmit tests to. |  1019     output_stream: A stream to write output from presubmit tests to. | 
|  1017     input_stream: A stream to read input from the user. |  1020     input_stream: A stream to read input from the user. | 
|  1018     default_presubmit: A default presubmit script to execute in any case. |  1021     default_presubmit: A default presubmit script to execute in any case. | 
|  1019     may_prompt: Enable (y/n) questions on warning or error. |  1022     may_prompt: Enable (y/n) questions on warning or error. | 
 |  1023     tbr: was --tbr specified to skip any reviewer/owner checks? | 
 |  1024     host_url: scheme, host, and port of host to use for rietveld-related | 
 |  1025         checks | 
|  1020  |  1026  | 
|  1021   Warning: |  1027   Warning: | 
|  1022     If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream |  1028     If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream | 
|  1023     SHOULD be sys.stdin. |  1029     SHOULD be sys.stdin. | 
|  1024  |  1030  | 
|  1025   Return: |  1031   Return: | 
|  1026     True if execution can continue, False if not. |  1032     True if execution can continue, False if not. | 
|  1027   """ |  1033   """ | 
|  1028   print "Running presubmit hooks..." |  1034   print "Running presubmit hooks..." | 
|  1029   start_time = time.time() |  1035   start_time = time.time() | 
|  1030   presubmit_files = ListRelevantPresubmitFiles(change.AbsoluteLocalPaths(True), |  1036   presubmit_files = ListRelevantPresubmitFiles(change.AbsoluteLocalPaths(True), | 
|  1031                                                change.RepositoryRoot()) |  1037                                                change.RepositoryRoot()) | 
|  1032   if not presubmit_files and verbose: |  1038   if not presubmit_files and verbose: | 
|  1033     output_stream.write("Warning, no presubmit.py found.\n") |  1039     output_stream.write("Warning, no presubmit.py found.\n") | 
|  1034   results = [] |  1040   results = [] | 
|  1035   executer = PresubmitExecuter(change, committing) |  1041   executer = PresubmitExecuter(change, committing, tbr, host_url) | 
|  1036   if default_presubmit: |  1042   if default_presubmit: | 
|  1037     if verbose: |  1043     if verbose: | 
|  1038       output_stream.write("Running default presubmit script.\n") |  1044       output_stream.write("Running default presubmit script.\n") | 
|  1039     fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') |  1045     fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') | 
|  1040     results += executer.ExecPresubmitScript(default_presubmit, fake_path) |  1046     results += executer.ExecPresubmitScript(default_presubmit, fake_path) | 
|  1041   for filename in presubmit_files: |  1047   for filename in presubmit_files: | 
|  1042     filename = os.path.abspath(filename) |  1048     filename = os.path.abspath(filename) | 
|  1043     if verbose: |  1049     if verbose: | 
|  1044       output_stream.write("Running %s\n" % filename) |  1050       output_stream.write("Running %s\n" % filename) | 
|  1045     # Accept CRLF presubmit script. |  1051     # Accept CRLF presubmit script. | 
| (...skipping 11 matching lines...) Expand all  Loading... | 
|  1057     else: |  1063     else: | 
|  1058       errors.append(result) |  1064       errors.append(result) | 
|  1059  |  1065  | 
|  1060   error_count = 0 |  1066   error_count = 0 | 
|  1061   for name, items in (('Messages', notifications), |  1067   for name, items in (('Messages', notifications), | 
|  1062                       ('Warnings', warnings), |  1068                       ('Warnings', warnings), | 
|  1063                       ('ERRORS', errors)): |  1069                       ('ERRORS', errors)): | 
|  1064     if items: |  1070     if items: | 
|  1065       output_stream.write('** Presubmit %s **\n' % name) |  1071       output_stream.write('** Presubmit %s **\n' % name) | 
|  1066       for item in items: |  1072       for item in items: | 
|  1067         if not item.IsMessage(): |  | 
|  1068           continue |  | 
|  1069  |  | 
|  1070         # Access to a protected member XXX of a client class |  1073         # Access to a protected member XXX of a client class | 
|  1071         # pylint: disable=W0212 |  1074         # pylint: disable=W0212 | 
|  1072         if not item._Handle(output_stream, input_stream, |  1075         if not item._Handle(output_stream, input_stream, | 
|  1073                             may_prompt=False): |  1076                             may_prompt=False): | 
|  1074           error_count += 1 |  1077           error_count += 1 | 
|  1075         output_stream.write('\n') |  1078         output_stream.write('\n') | 
|  1076  |  1079  | 
|  1077   total_time = time.time() - start_time |  1080   total_time = time.time() - start_time | 
|  1078   if total_time > 1.0: |  1081   if total_time > 1.0: | 
|  1079     print "Presubmit checks took %.1fs to calculate." % total_time |  1082     print "Presubmit checks took %.1fs to calculate." % total_time | 
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  1185                                options.commit, |  1188                                options.commit, | 
|  1186                                options.verbose, |  1189                                options.verbose, | 
|  1187                                sys.stdout, |  1190                                sys.stdout, | 
|  1188                                sys.stdin, |  1191                                sys.stdin, | 
|  1189                                options.default_presubmit, |  1192                                options.default_presubmit, | 
|  1190                                options.may_prompt) |  1193                                options.may_prompt) | 
|  1191  |  1194  | 
|  1192  |  1195  | 
|  1193 if __name__ == '__main__': |  1196 if __name__ == '__main__': | 
|  1194   sys.exit(Main(sys.argv)) |  1197   sys.exit(Main(sys.argv)) | 
| OLD | NEW |