| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2006-2009 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.1' | 9 __version__ = '1.1' |
| 10 | 10 |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 223 return depot_path | 223 return depot_path |
| 224 | 224 |
| 225 def AffectedFiles(self, include_dirs=False, include_deletes=True): | 225 def AffectedFiles(self, include_dirs=False, include_deletes=True): |
| 226 """Same as input_api.change.AffectedFiles() except only lists files | 226 """Same as input_api.change.AffectedFiles() except only lists files |
| 227 (and optionally directories) in the same directory as the current presubmit | 227 (and optionally directories) in the same directory as the current presubmit |
| 228 script, or subdirectories thereof. | 228 script, or subdirectories thereof. |
| 229 """ | 229 """ |
| 230 dir_with_slash = normpath("%s/" % self.PresubmitLocalPath()) | 230 dir_with_slash = normpath("%s/" % self.PresubmitLocalPath()) |
| 231 if len(dir_with_slash) == 1: | 231 if len(dir_with_slash) == 1: |
| 232 dir_with_slash = '' | 232 dir_with_slash = '' |
| 233 return filter( | 233 return filter(lambda x: normpath(x.LocalPath()).startswith(dir_with_slash), |
| 234 lambda x: normpath(x.AbsoluteLocalPath()).startswith(dir_with_slash), | 234 self.change.AffectedFiles(include_dirs, include_deletes)) |
| 235 self.change.AffectedFiles(include_dirs, include_deletes)) | |
| 236 | 235 |
| 237 def LocalPaths(self, include_dirs=False): | 236 def LocalPaths(self, include_dirs=False): |
| 238 """Returns local paths of input_api.AffectedFiles().""" | 237 """Returns local paths of input_api.AffectedFiles().""" |
| 239 return [af.LocalPath() for af in self.AffectedFiles(include_dirs)] | 238 return [af.LocalPath() for af in self.AffectedFiles(include_dirs)] |
| 240 | 239 |
| 241 def AbsoluteLocalPaths(self, include_dirs=False): | 240 def AbsoluteLocalPaths(self, include_dirs=False): |
| 242 """Returns absolute local paths of input_api.AffectedFiles().""" | 241 """Returns absolute local paths of input_api.AffectedFiles().""" |
| 243 return [af.AbsoluteLocalPath() for af in self.AffectedFiles(include_dirs)] | 242 return [af.AbsoluteLocalPath() for af in self.AffectedFiles(include_dirs)] |
| 244 | 243 |
| 245 def ServerPaths(self, include_dirs=False): | 244 def ServerPaths(self, include_dirs=False): |
| (...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 542 a 3 tuple: | 541 a 3 tuple: |
| 543 the AffectedFile instance of the current file; | 542 the AffectedFile instance of the current file; |
| 544 integer line number (1-based); and | 543 integer line number (1-based); and |
| 545 the contents of the line as a string. | 544 the contents of the line as a string. |
| 546 """ | 545 """ |
| 547 return InputApi._RightHandSideLinesImpl( | 546 return InputApi._RightHandSideLinesImpl( |
| 548 filter(lambda x: x.IsTextFile(), | 547 filter(lambda x: x.IsTextFile(), |
| 549 self.AffectedFiles(include_deletes=False))) | 548 self.AffectedFiles(include_deletes=False))) |
| 550 | 549 |
| 551 | 550 |
| 552 def ListRelevantPresubmitFiles(files, root): | 551 def ListRelevantPresubmitFiles(files): |
| 553 """Finds all presubmit files that apply to a given set of source files. | 552 """Finds all presubmit files that apply to a given set of source files. |
| 554 | 553 |
| 555 Args: | 554 Args: |
| 556 files: An iterable container containing file paths. | 555 files: An iterable container containing file paths. |
| 557 root: Path where to stop searching. | |
| 558 | 556 |
| 559 Return: | 557 Return: |
| 560 List of absolute paths of the existing PRESUBMIT.py scripts. | 558 ['foo/blat/PRESUBMIT.py', 'mat/gat/PRESUBMIT.py'] |
| 561 """ | 559 """ |
| 562 entries = [] | 560 checked_dirs = {} # Keys are directory paths, values are ignored. |
| 563 for f in files: | 561 source_dirs = [os.path.dirname(f) for f in files] |
| 564 f = normpath(os.path.join(root, f)) | 562 presubmit_files = [] |
| 565 while f: | 563 for dir in source_dirs: |
| 566 f = os.path.dirname(f) | 564 while (True): |
| 567 if f in entries: | 565 if dir in checked_dirs: |
| 566 break # We've already walked up from this directory. |
| 567 |
| 568 test_path = os.path.join(dir, 'PRESUBMIT.py') |
| 569 if os.path.isfile(test_path): |
| 570 presubmit_files.append(normpath(test_path)) |
| 571 |
| 572 checked_dirs[dir] = '' |
| 573 if dir in ['', '.']: |
| 568 break | 574 break |
| 569 entries.append(f) | 575 else: |
| 570 if f == root: | 576 dir = os.path.dirname(dir) |
| 571 break | 577 return presubmit_files |
| 572 entries.sort() | |
| 573 entries = map(lambda x: os.path.join(x, 'PRESUBMIT.py'), entries) | |
| 574 return filter(lambda x: os.path.isfile(x), entries) | |
| 575 | 578 |
| 576 | 579 |
| 577 class PresubmitExecuter(object): | 580 class PresubmitExecuter(object): |
| 578 def __init__(self, change_info, committing): | 581 def __init__(self, change_info, committing): |
| 579 """ | 582 """ |
| 580 Args: | 583 Args: |
| 581 change_info: The ChangeInfo object for the change. | 584 change_info: The ChangeInfo object for the change. |
| 582 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 585 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 583 """ | 586 """ |
| 584 # TODO(maruel): Determine the SCM. | 587 # TODO(maruel): Determine the SCM. |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 643 change_info: The ChangeInfo object for the change. | 646 change_info: The ChangeInfo object for the change. |
| 644 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 647 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 645 verbose: Prints debug info. | 648 verbose: Prints debug info. |
| 646 output_stream: A stream to write output from presubmit tests to. | 649 output_stream: A stream to write output from presubmit tests to. |
| 647 input_stream: A stream to read input from the user. | 650 input_stream: A stream to read input from the user. |
| 648 default_presubmit: A default presubmit script to execute in any case. | 651 default_presubmit: A default presubmit script to execute in any case. |
| 649 | 652 |
| 650 Return: | 653 Return: |
| 651 True if execution can continue, False if not. | 654 True if execution can continue, False if not. |
| 652 """ | 655 """ |
| 653 checkout_root = gcl.GetRepositoryRoot() | 656 presubmit_files = ListRelevantPresubmitFiles(change_info.FileList()) |
| 654 presubmit_files = ListRelevantPresubmitFiles(change_info.FileList(), | |
| 655 checkout_root) | |
| 656 if not presubmit_files and verbose: | 657 if not presubmit_files and verbose: |
| 657 output_stream.write("Warning, no presubmit.py found.\n") | 658 output_stream.write("Warning, no presubmit.py found.\n") |
| 658 results = [] | 659 results = [] |
| 659 executer = PresubmitExecuter(change_info, committing) | 660 executer = PresubmitExecuter(change_info, committing) |
| 660 if default_presubmit: | 661 if default_presubmit: |
| 661 if verbose: | 662 if verbose: |
| 662 output_stream.write("Running default presubmit script.\n") | 663 output_stream.write("Running default presubmit script.\n") |
| 663 fake_path = os.path.join(checkout_root, 'PRESUBMIT.py') | 664 results += executer.ExecPresubmitScript(default_presubmit, 'PRESUBMIT.py') |
| 664 results += executer.ExecPresubmitScript(default_presubmit, fake_path) | |
| 665 for filename in presubmit_files: | 665 for filename in presubmit_files: |
| 666 filename = os.path.abspath(filename) | 666 filename = os.path.abspath(filename) |
| 667 if verbose: | 667 if verbose: |
| 668 output_stream.write("Running %s\n" % filename) | 668 output_stream.write("Running %s\n" % filename) |
| 669 # Accept CRLF presubmit script. | 669 # Accept CRLF presubmit script. |
| 670 presubmit_script = gcl.ReadFile(filename, 'rU') | 670 presubmit_script = gcl.ReadFile(filename, 'rU') |
| 671 results += executer.ExecPresubmitScript(presubmit_script, filename) | 671 results += executer.ExecPresubmitScript(presubmit_script, filename) |
| 672 | 672 |
| 673 errors = [] | 673 errors = [] |
| 674 notifications = [] | 674 notifications = [] |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 738 return not DoPresubmitChecks(gcl.ChangeInfo(name='temp', files=files), | 738 return not DoPresubmitChecks(gcl.ChangeInfo(name='temp', files=files), |
| 739 options.commit, | 739 options.commit, |
| 740 options.verbose, | 740 options.verbose, |
| 741 sys.stdout, | 741 sys.stdout, |
| 742 sys.stdin, | 742 sys.stdin, |
| 743 default_presubmit=None) | 743 default_presubmit=None) |
| 744 | 744 |
| 745 | 745 |
| 746 if __name__ == '__main__': | 746 if __name__ == '__main__': |
| 747 sys.exit(Main(sys.argv)) | 747 sys.exit(Main(sys.argv)) |
| OLD | NEW |