| 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.6.1' | 9 __version__ = '1.6.1' |
| 10 | 10 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 except ImportError: | 40 except ImportError: |
| 41 # Import the one included in depot_tools. | 41 # Import the one included in depot_tools. |
| 42 sys.path.append(os.path.join(os.path.dirname(__file__), 'third_party')) | 42 sys.path.append(os.path.join(os.path.dirname(__file__), 'third_party')) |
| 43 import simplejson as json # pylint: disable=F0401 | 43 import simplejson as json # pylint: disable=F0401 |
| 44 | 44 |
| 45 # Local imports. | 45 # Local imports. |
| 46 import fix_encoding | 46 import fix_encoding |
| 47 import gclient_utils | 47 import gclient_utils |
| 48 import owners | 48 import owners |
| 49 import presubmit_canned_checks | 49 import presubmit_canned_checks |
| 50 import rietveld |
| 50 import scm | 51 import scm |
| 51 import subprocess2 as subprocess # Exposed through the API. | 52 import subprocess2 as subprocess # Exposed through the API. |
| 52 | 53 |
| 53 | 54 |
| 54 # Ask for feedback only once in program lifetime. | 55 # Ask for feedback only once in program lifetime. |
| 55 _ASKED_FOR_FEEDBACK = False | 56 _ASKED_FOR_FEEDBACK = False |
| 56 | 57 |
| 57 | 58 |
| 58 class PresubmitFailure(Exception): | 59 class PresubmitFailure(Exception): |
| 59 pass | 60 pass |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 209 r".*\bxcodebuild[\\\/].*", | 210 r".*\bxcodebuild[\\\/].*", |
| 210 r".*\bsconsbuild[\\\/].*", | 211 r".*\bsconsbuild[\\\/].*", |
| 211 # All caps files like README and LICENCE. | 212 # All caps files like README and LICENCE. |
| 212 r".*\b[A-Z0-9_]{2,}$", | 213 r".*\b[A-Z0-9_]{2,}$", |
| 213 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) | 214 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) |
| 214 r"(|.*[\\\/])\.git[\\\/].*", | 215 r"(|.*[\\\/])\.git[\\\/].*", |
| 215 r"(|.*[\\\/])\.svn[\\\/].*", | 216 r"(|.*[\\\/])\.svn[\\\/].*", |
| 216 ) | 217 ) |
| 217 | 218 |
| 218 def __init__(self, change, presubmit_path, is_committing, tbr, | 219 def __init__(self, change, presubmit_path, is_committing, tbr, |
| 219 rietveld, verbose): | 220 rietveld_obj, verbose): |
| 220 """Builds an InputApi object. | 221 """Builds an InputApi object. |
| 221 | 222 |
| 222 Args: | 223 Args: |
| 223 change: A presubmit.Change object. | 224 change: A presubmit.Change object. |
| 224 presubmit_path: The path to the presubmit script being processed. | 225 presubmit_path: The path to the presubmit script being processed. |
| 225 is_committing: True if the change is about to be committed. | 226 is_committing: True if the change is about to be committed. |
| 226 tbr: True if '--tbr' was passed to skip any reviewer/owner checks | 227 tbr: True if '--tbr' was passed to skip any reviewer/owner checks |
| 227 rietveld: rietveld client object | 228 rietveld_obj: rietveld.Rietveld client object |
| 228 """ | 229 """ |
| 229 # Version number of the presubmit_support script. | 230 # Version number of the presubmit_support script. |
| 230 self.version = [int(x) for x in __version__.split('.')] | 231 self.version = [int(x) for x in __version__.split('.')] |
| 231 self.change = change | 232 self.change = change |
| 232 self.is_committing = is_committing | 233 self.is_committing = is_committing |
| 233 self.tbr = tbr | 234 self.tbr = tbr |
| 234 self.rietveld = rietveld | 235 self.rietveld = rietveld_obj |
| 235 # TBD | 236 # TBD |
| 236 self.host_url = 'http://codereview.chromium.org' | 237 self.host_url = 'http://codereview.chromium.org' |
| 237 if self.rietveld: | 238 if self.rietveld: |
| 238 self.host_url = rietveld.url | 239 self.host_url = self.rietveld.url |
| 239 | 240 |
| 240 # We expose various modules and functions as attributes of the input_api | 241 # We expose various modules and functions as attributes of the input_api |
| 241 # so that presubmit scripts don't have to import them. | 242 # so that presubmit scripts don't have to import them. |
| 242 self.basename = os.path.basename | 243 self.basename = os.path.basename |
| 243 self.cPickle = cPickle | 244 self.cPickle = cPickle |
| 244 self.cStringIO = cStringIO | 245 self.cStringIO = cStringIO |
| 245 self.json = json | 246 self.json = json |
| 246 self.logging = logging.getLogger('PRESUBMIT') | 247 self.logging = logging.getLogger('PRESUBMIT') |
| 247 self.os_listdir = os.listdir | 248 self.os_listdir = os.listdir |
| 248 self.os_walk = os.walk | 249 self.os_walk = os.walk |
| (...skipping 699 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 948 presubmit_script, filename, project) | 949 presubmit_script, filename, project) |
| 949 | 950 |
| 950 slaves = list(set(results)) | 951 slaves = list(set(results)) |
| 951 if slaves and verbose: | 952 if slaves and verbose: |
| 952 output_stream.write(', '.join(slaves)) | 953 output_stream.write(', '.join(slaves)) |
| 953 output_stream.write('\n') | 954 output_stream.write('\n') |
| 954 return slaves | 955 return slaves |
| 955 | 956 |
| 956 | 957 |
| 957 class PresubmitExecuter(object): | 958 class PresubmitExecuter(object): |
| 958 def __init__(self, change, committing, tbr, rietveld, verbose): | 959 def __init__(self, change, committing, tbr, rietveld_obj, verbose): |
| 959 """ | 960 """ |
| 960 Args: | 961 Args: |
| 961 change: The Change object. | 962 change: The Change object. |
| 962 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 963 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 963 tbr: True if '--tbr' was passed to skip any reviewer/owner checks | 964 tbr: True if '--tbr' was passed to skip any reviewer/owner checks |
| 964 rietveld: rietveld client object. | 965 rietveld_obj: rietveld.Rietveld client object. |
| 965 """ | 966 """ |
| 966 self.change = change | 967 self.change = change |
| 967 self.committing = committing | 968 self.committing = committing |
| 968 self.tbr = tbr | 969 self.tbr = tbr |
| 969 self.rietveld = rietveld | 970 self.rietveld = rietveld_obj |
| 970 self.verbose = verbose | 971 self.verbose = verbose |
| 971 | 972 |
| 972 def ExecPresubmitScript(self, script_text, presubmit_path): | 973 def ExecPresubmitScript(self, script_text, presubmit_path): |
| 973 """Executes a single presubmit script. | 974 """Executes a single presubmit script. |
| 974 | 975 |
| 975 Args: | 976 Args: |
| 976 script_text: The text of the presubmit script. | 977 script_text: The text of the presubmit script. |
| 977 presubmit_path: The path to the presubmit file (this will be reported via | 978 presubmit_path: The path to the presubmit file (this will be reported via |
| 978 input_api.PresubmitLocalPath()). | 979 input_api.PresubmitLocalPath()). |
| 979 | 980 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1023 | 1024 |
| 1024 | 1025 |
| 1025 def DoPresubmitChecks(change, | 1026 def DoPresubmitChecks(change, |
| 1026 committing, | 1027 committing, |
| 1027 verbose, | 1028 verbose, |
| 1028 output_stream, | 1029 output_stream, |
| 1029 input_stream, | 1030 input_stream, |
| 1030 default_presubmit, | 1031 default_presubmit, |
| 1031 may_prompt, | 1032 may_prompt, |
| 1032 tbr, | 1033 tbr, |
| 1033 rietveld): | 1034 rietveld_obj): |
| 1034 """Runs all presubmit checks that apply to the files in the change. | 1035 """Runs all presubmit checks that apply to the files in the change. |
| 1035 | 1036 |
| 1036 This finds all PRESUBMIT.py files in directories enclosing the files in the | 1037 This finds all PRESUBMIT.py files in directories enclosing the files in the |
| 1037 change (up to the repository root) and calls the relevant entrypoint function | 1038 change (up to the repository root) and calls the relevant entrypoint function |
| 1038 depending on whether the change is being committed or uploaded. | 1039 depending on whether the change is being committed or uploaded. |
| 1039 | 1040 |
| 1040 Prints errors, warnings and notifications. Prompts the user for warnings | 1041 Prints errors, warnings and notifications. Prompts the user for warnings |
| 1041 when needed. | 1042 when needed. |
| 1042 | 1043 |
| 1043 Args: | 1044 Args: |
| 1044 change: The Change object. | 1045 change: The Change object. |
| 1045 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 1046 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 1046 verbose: Prints debug info. | 1047 verbose: Prints debug info. |
| 1047 output_stream: A stream to write output from presubmit tests to. | 1048 output_stream: A stream to write output from presubmit tests to. |
| 1048 input_stream: A stream to read input from the user. | 1049 input_stream: A stream to read input from the user. |
| 1049 default_presubmit: A default presubmit script to execute in any case. | 1050 default_presubmit: A default presubmit script to execute in any case. |
| 1050 may_prompt: Enable (y/n) questions on warning or error. | 1051 may_prompt: Enable (y/n) questions on warning or error. |
| 1051 tbr: was --tbr specified to skip any reviewer/owner checks? | 1052 tbr: was --tbr specified to skip any reviewer/owner checks? |
| 1052 rietveld: rietveld object. | 1053 rietveld_obj: rietveld.Rietveld object. |
| 1053 | 1054 |
| 1054 Warning: | 1055 Warning: |
| 1055 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream | 1056 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream |
| 1056 SHOULD be sys.stdin. | 1057 SHOULD be sys.stdin. |
| 1057 | 1058 |
| 1058 Return: | 1059 Return: |
| 1059 A PresubmitOutput object. Use output.should_continue() to figure out | 1060 A PresubmitOutput object. Use output.should_continue() to figure out |
| 1060 if there were errors or warnings and the caller should abort. | 1061 if there were errors or warnings and the caller should abort. |
| 1061 """ | 1062 """ |
| 1062 old_environ = os.environ | 1063 old_environ = os.environ |
| 1063 try: | 1064 try: |
| 1064 # Make sure python subprocesses won't generate .pyc files. | 1065 # Make sure python subprocesses won't generate .pyc files. |
| 1065 os.environ = os.environ.copy() | 1066 os.environ = os.environ.copy() |
| 1066 os.environ['PYTHONDONTWRITEBYTECODE'] = '1' | 1067 os.environ['PYTHONDONTWRITEBYTECODE'] = '1' |
| 1067 | 1068 |
| 1068 output = PresubmitOutput(input_stream, output_stream) | 1069 output = PresubmitOutput(input_stream, output_stream) |
| 1069 if committing: | 1070 if committing: |
| 1070 output.write("Running presubmit commit checks ...\n") | 1071 output.write("Running presubmit commit checks ...\n") |
| 1071 else: | 1072 else: |
| 1072 output.write("Running presubmit upload checks ...\n") | 1073 output.write("Running presubmit upload checks ...\n") |
| 1073 start_time = time.time() | 1074 start_time = time.time() |
| 1074 presubmit_files = ListRelevantPresubmitFiles( | 1075 presubmit_files = ListRelevantPresubmitFiles( |
| 1075 change.AbsoluteLocalPaths(True), change.RepositoryRoot()) | 1076 change.AbsoluteLocalPaths(True), change.RepositoryRoot()) |
| 1076 if not presubmit_files and verbose: | 1077 if not presubmit_files and verbose: |
| 1077 output.write("Warning, no presubmit.py found.\n") | 1078 output.write("Warning, no presubmit.py found.\n") |
| 1078 results = [] | 1079 results = [] |
| 1079 executer = PresubmitExecuter(change, committing, tbr, rietveld, verbose) | 1080 executer = PresubmitExecuter(change, committing, tbr, rietveld_obj, verbose) |
| 1080 if default_presubmit: | 1081 if default_presubmit: |
| 1081 if verbose: | 1082 if verbose: |
| 1082 output.write("Running default presubmit script.\n") | 1083 output.write("Running default presubmit script.\n") |
| 1083 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') | 1084 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') |
| 1084 results += executer.ExecPresubmitScript(default_presubmit, fake_path) | 1085 results += executer.ExecPresubmitScript(default_presubmit, fake_path) |
| 1085 for filename in presubmit_files: | 1086 for filename in presubmit_files: |
| 1086 filename = os.path.abspath(filename) | 1087 filename = os.path.abspath(filename) |
| 1087 if verbose: | 1088 if verbose: |
| 1088 output.write("Running %s\n" % filename) | 1089 output.write("Running %s\n" % filename) |
| 1089 # Accept CRLF presubmit script. | 1090 # Accept CRLF presubmit script. |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1198 parser.add_option("--description", default='') | 1199 parser.add_option("--description", default='') |
| 1199 parser.add_option("--issue", type='int', default=0) | 1200 parser.add_option("--issue", type='int', default=0) |
| 1200 parser.add_option("--patchset", type='int', default=0) | 1201 parser.add_option("--patchset", type='int', default=0) |
| 1201 parser.add_option("--root", default=os.getcwd(), | 1202 parser.add_option("--root", default=os.getcwd(), |
| 1202 help="Search for PRESUBMIT.py up to this directory. " | 1203 help="Search for PRESUBMIT.py up to this directory. " |
| 1203 "If inherit-review-settings-ok is present in this " | 1204 "If inherit-review-settings-ok is present in this " |
| 1204 "directory, parent directories up to the root file " | 1205 "directory, parent directories up to the root file " |
| 1205 "system directories will also be searched.") | 1206 "system directories will also be searched.") |
| 1206 parser.add_option("--default_presubmit") | 1207 parser.add_option("--default_presubmit") |
| 1207 parser.add_option("--may_prompt", action='store_true', default=False) | 1208 parser.add_option("--may_prompt", action='store_true', default=False) |
| 1209 parser.add_option("--rietveld_url", help=optparse.SUPPRESS_HELP) |
| 1210 parser.add_option("--rietveld_email", help=optparse.SUPPRESS_HELP) |
| 1211 parser.add_option("--rietveld_password", help=optparse.SUPPRESS_HELP) |
| 1208 options, args = parser.parse_args(argv) | 1212 options, args = parser.parse_args(argv) |
| 1209 if options.verbose >= 2: | 1213 if options.verbose >= 2: |
| 1210 logging.basicConfig(level=logging.DEBUG) | 1214 logging.basicConfig(level=logging.DEBUG) |
| 1211 elif options.verbose: | 1215 elif options.verbose: |
| 1212 logging.basicConfig(level=logging.INFO) | 1216 logging.basicConfig(level=logging.INFO) |
| 1213 else: | 1217 else: |
| 1214 logging.basicConfig(level=logging.ERROR) | 1218 logging.basicConfig(level=logging.ERROR) |
| 1215 change_class, files = load_files(options, args) | 1219 change_class, files = load_files(options, args) |
| 1216 if not change_class: | 1220 if not change_class: |
| 1217 parser.error('For unversioned directory, <files> is not optional.') | 1221 parser.error('For unversioned directory, <files> is not optional.') |
| 1218 logging.info('Found %d file(s).' % len(files)) | 1222 logging.info('Found %d file(s).' % len(files)) |
| 1223 rietveld_obj = None |
| 1224 if options.rietveld_url: |
| 1225 rietveld_obj = rietveld.Rietveld( |
| 1226 options.rietveld_url, |
| 1227 options.rietveld_email, |
| 1228 options.rietveld_password) |
| 1219 try: | 1229 try: |
| 1220 results = DoPresubmitChecks( | 1230 results = DoPresubmitChecks( |
| 1221 change_class(options.name, | 1231 change_class(options.name, |
| 1222 options.description, | 1232 options.description, |
| 1223 options.root, | 1233 options.root, |
| 1224 files, | 1234 files, |
| 1225 options.issue, | 1235 options.issue, |
| 1226 options.patchset, | 1236 options.patchset, |
| 1227 options.author), | 1237 options.author), |
| 1228 options.commit, | 1238 options.commit, |
| 1229 options.verbose, | 1239 options.verbose, |
| 1230 sys.stdout, | 1240 sys.stdout, |
| 1231 sys.stdin, | 1241 sys.stdin, |
| 1232 options.default_presubmit, | 1242 options.default_presubmit, |
| 1233 options.may_prompt, | 1243 options.may_prompt, |
| 1234 False, | 1244 False, |
| 1235 None) | 1245 rietveld_obj) |
| 1236 return not results.should_continue() | 1246 return not results.should_continue() |
| 1237 except PresubmitFailure, e: | 1247 except PresubmitFailure, e: |
| 1238 print >> sys.stderr, e | 1248 print >> sys.stderr, e |
| 1239 print >> sys.stderr, 'Maybe your depot_tools is out of date?' | 1249 print >> sys.stderr, 'Maybe your depot_tools is out of date?' |
| 1240 print >> sys.stderr, 'If all fails, contact maruel@' | 1250 print >> sys.stderr, 'If all fails, contact maruel@' |
| 1241 return 2 | 1251 return 2 |
| 1242 | 1252 |
| 1243 | 1253 |
| 1244 if __name__ == '__main__': | 1254 if __name__ == '__main__': |
| 1245 fix_encoding.fix_encoding() | 1255 fix_encoding.fix_encoding() |
| 1246 sys.exit(Main(None)) | 1256 sys.exit(Main(None)) |
| OLD | NEW |