| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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.8.0' | 9 __version__ = '1.8.0' |
| 10 | 10 |
| (...skipping 1269 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1280 """Executes a single presubmit script. | 1280 """Executes a single presubmit script. |
| 1281 | 1281 |
| 1282 Args: | 1282 Args: |
| 1283 script_text: The text of the presubmit script. | 1283 script_text: The text of the presubmit script. |
| 1284 presubmit_path: The path to the presubmit file (this will be reported via | 1284 presubmit_path: The path to the presubmit file (this will be reported via |
| 1285 input_api.PresubmitLocalPath()). | 1285 input_api.PresubmitLocalPath()). |
| 1286 | 1286 |
| 1287 Return: | 1287 Return: |
| 1288 A list of result objects, empty if no problems. | 1288 A list of result objects, empty if no problems. |
| 1289 """ | 1289 """ |
| 1290 |
| 1290 # Change to the presubmit file's directory to support local imports. | 1291 # Change to the presubmit file's directory to support local imports. |
| 1291 main_path = os.getcwd() | 1292 main_path = os.getcwd() |
| 1292 os.chdir(os.path.dirname(presubmit_path)) | 1293 os.chdir(os.path.dirname(presubmit_path)) |
| 1293 | 1294 |
| 1294 # Load the presubmit script into context. | 1295 # Load the presubmit script into context. |
| 1295 input_api = InputApi(self.change, presubmit_path, self.committing, | 1296 input_api = InputApi(self.change, presubmit_path, self.committing, |
| 1296 self.rietveld, self.verbose) | 1297 self.rietveld, self.verbose) |
| 1297 context = {} | 1298 context = {} |
| 1298 try: | 1299 try: |
| 1299 exec script_text in context | 1300 exec script_text in context |
| (...skipping 28 matching lines...) Expand all Loading... |
| 1328 return result | 1329 return result |
| 1329 | 1330 |
| 1330 | 1331 |
| 1331 def DoPresubmitChecks(change, | 1332 def DoPresubmitChecks(change, |
| 1332 committing, | 1333 committing, |
| 1333 verbose, | 1334 verbose, |
| 1334 output_stream, | 1335 output_stream, |
| 1335 input_stream, | 1336 input_stream, |
| 1336 default_presubmit, | 1337 default_presubmit, |
| 1337 may_prompt, | 1338 may_prompt, |
| 1338 rietveld_obj, | 1339 rietveld_obj): |
| 1339 cleanup=False): | |
| 1340 """Runs all presubmit checks that apply to the files in the change. | 1340 """Runs all presubmit checks that apply to the files in the change. |
| 1341 | 1341 |
| 1342 This finds all PRESUBMIT.py files in directories enclosing the files in the | 1342 This finds all PRESUBMIT.py files in directories enclosing the files in the |
| 1343 change (up to the repository root) and calls the relevant entrypoint function | 1343 change (up to the repository root) and calls the relevant entrypoint function |
| 1344 depending on whether the change is being committed or uploaded. | 1344 depending on whether the change is being committed or uploaded. |
| 1345 | 1345 |
| 1346 Prints errors, warnings and notifications. Prompts the user for warnings | 1346 Prints errors, warnings and notifications. Prompts the user for warnings |
| 1347 when needed. | 1347 when needed. |
| 1348 | 1348 |
| 1349 Args: | 1349 Args: |
| 1350 change: The Change object. | 1350 change: The Change object. |
| 1351 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 1351 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 1352 verbose: Prints debug info. | 1352 verbose: Prints debug info. |
| 1353 output_stream: A stream to write output from presubmit tests to. | 1353 output_stream: A stream to write output from presubmit tests to. |
| 1354 input_stream: A stream to read input from the user. | 1354 input_stream: A stream to read input from the user. |
| 1355 default_presubmit: A default presubmit script to execute in any case. | 1355 default_presubmit: A default presubmit script to execute in any case. |
| 1356 may_prompt: Enable (y/n) questions on warning or error. | 1356 may_prompt: Enable (y/n) questions on warning or error. |
| 1357 rietveld_obj: rietveld.Rietveld object. | 1357 rietveld_obj: rietveld.Rietveld object. |
| 1358 cleanup: If True, filesystem modifications may be made as necessary to | |
| 1359 clean up repository state to ensure reliable checks. | |
| 1360 | 1358 |
| 1361 Warning: | 1359 Warning: |
| 1362 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream | 1360 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream |
| 1363 SHOULD be sys.stdin. | 1361 SHOULD be sys.stdin. |
| 1364 | 1362 |
| 1365 Return: | 1363 Return: |
| 1366 A PresubmitOutput object. Use output.should_continue() to figure out | 1364 A PresubmitOutput object. Use output.should_continue() to figure out |
| 1367 if there were errors or warnings and the caller should abort. | 1365 if there were errors or warnings and the caller should abort. |
| 1368 """ | 1366 """ |
| 1369 old_environ = os.environ | 1367 old_environ = os.environ |
| 1370 try: | 1368 try: |
| 1371 # Clear all orphaned compiled Python files. | |
| 1372 CleanOrphanedCompiledPython(output_stream, change.RepositoryRoot(), | |
| 1373 remove=cleanup) | |
| 1374 | |
| 1375 # Make sure python subprocesses won't generate .pyc files. | 1369 # Make sure python subprocesses won't generate .pyc files. |
| 1376 os.environ = os.environ.copy() | 1370 os.environ = os.environ.copy() |
| 1377 os.environ['PYTHONDONTWRITEBYTECODE'] = '1' | 1371 os.environ['PYTHONDONTWRITEBYTECODE'] = '1' |
| 1378 | 1372 |
| 1379 output = PresubmitOutput(input_stream, output_stream) | 1373 output = PresubmitOutput(input_stream, output_stream) |
| 1380 if committing: | 1374 if committing: |
| 1381 output.write("Running presubmit commit checks ...\n") | 1375 output.write("Running presubmit commit checks ...\n") |
| 1382 else: | 1376 else: |
| 1383 output.write("Running presubmit upload checks ...\n") | 1377 output.write("Running presubmit upload checks ...\n") |
| 1384 start_time = time.time() | 1378 start_time = time.time() |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1441 output.write( | 1435 output.write( |
| 1442 'Was the presubmit check useful? If not, run "git cl presubmit -v"\n' | 1436 'Was the presubmit check useful? If not, run "git cl presubmit -v"\n' |
| 1443 'to figure out which PRESUBMIT.py was run, then run git blame\n' | 1437 'to figure out which PRESUBMIT.py was run, then run git blame\n' |
| 1444 'on the file to figure out who to ask for help.\n') | 1438 'on the file to figure out who to ask for help.\n') |
| 1445 _ASKED_FOR_FEEDBACK = True | 1439 _ASKED_FOR_FEEDBACK = True |
| 1446 return output | 1440 return output |
| 1447 finally: | 1441 finally: |
| 1448 os.environ = old_environ | 1442 os.environ = old_environ |
| 1449 | 1443 |
| 1450 | 1444 |
| 1451 def IsCompiledPython(path): | |
| 1452 """Tests whether a given path looks like a compiled Python file.""" | |
| 1453 # Validate the magic number. | |
| 1454 with open(path, 'rb') as fd: | |
| 1455 magic = fd.read(8) | |
| 1456 return len(magic) == 8 and magic[2:4] == '\x0d\x0a' | |
| 1457 | |
| 1458 | |
| 1459 def CleanOrphanedCompiledPython(output, path, remove): | |
| 1460 """Clears all compiled Python files in a directory recursively.""" | |
| 1461 pyc_files = [] | |
| 1462 for dirpath, _, filenames in os.walk(path): | |
| 1463 for filename in filenames: | |
| 1464 name, ext = os.path.splitext(filename) | |
| 1465 if ext != '.pyc': | |
| 1466 continue | |
| 1467 | |
| 1468 # Is there an accompanying Python script? If so, leave the compiled file. | |
| 1469 py_path = os.path.join(dirpath, '%s.py' % (name,)) | |
| 1470 if os.path.isfile(py_path): | |
| 1471 continue | |
| 1472 | |
| 1473 # Make sure this is compiled Python before we delete it. | |
| 1474 path = os.path.join(dirpath, filename) | |
| 1475 if not IsCompiledPython(path): | |
| 1476 continue | |
| 1477 pyc_files.append(path) | |
| 1478 if pyc_files and not remove: | |
| 1479 output.write( | |
| 1480 'Warning: Found orphaned compiled Python files. These should be\n' | |
| 1481 'removed before running PRESUBMIT, as they can have unexpected impact\n' | |
| 1482 'on checks and tests:\n') | |
| 1483 for pyc_file in pyc_files: | |
| 1484 output.write(' %s\n' % (pyc_file,)) | |
| 1485 else: | |
| 1486 for pyc_file in pyc_files: | |
| 1487 output.write('Removing orphaned compiled Python file: %s\n' % (pyc_file,)) | |
| 1488 os.remove(pyc_file) | |
| 1489 | |
| 1490 | |
| 1491 def ScanSubDirs(mask, recursive): | 1445 def ScanSubDirs(mask, recursive): |
| 1492 if not recursive: | 1446 if not recursive: |
| 1493 return [x for x in glob.glob(mask) if x not in ('.svn', '.git')] | 1447 return [x for x in glob.glob(mask) if x not in ('.svn', '.git')] |
| 1494 else: | 1448 else: |
| 1495 results = [] | 1449 results = [] |
| 1496 for root, dirs, files in os.walk('.'): | 1450 for root, dirs, files in os.walk('.'): |
| 1497 if '.svn' in dirs: | 1451 if '.svn' in dirs: |
| 1498 dirs.remove('.svn') | 1452 dirs.remove('.svn') |
| 1499 if '.git' in dirs: | 1453 if '.git' in dirs: |
| 1500 dirs.remove('.git') | 1454 dirs.remove('.git') |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1715 except PresubmitFailure, e: | 1669 except PresubmitFailure, e: |
| 1716 print >> sys.stderr, e | 1670 print >> sys.stderr, e |
| 1717 print >> sys.stderr, 'Maybe your depot_tools is out of date?' | 1671 print >> sys.stderr, 'Maybe your depot_tools is out of date?' |
| 1718 print >> sys.stderr, 'If all fails, contact maruel@' | 1672 print >> sys.stderr, 'If all fails, contact maruel@' |
| 1719 return 2 | 1673 return 2 |
| 1720 | 1674 |
| 1721 | 1675 |
| 1722 if __name__ == '__main__': | 1676 if __name__ == '__main__': |
| 1723 fix_encoding.fix_encoding() | 1677 fix_encoding.fix_encoding() |
| 1724 sys.exit(Main(None)) | 1678 sys.exit(Main(None)) |
| OLD | NEW |