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 |