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