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(lambda x: normpath(x.LocalPath()).startswith(dir_with_slash), | 233 return filter( |
234 self.change.AffectedFiles(include_dirs, include_deletes)) | 234 lambda x: normpath(x.AbsoluteLocalPath()).startswith(dir_with_slash), |
| 235 self.change.AffectedFiles(include_dirs, include_deletes)) |
235 | 236 |
236 def LocalPaths(self, include_dirs=False): | 237 def LocalPaths(self, include_dirs=False): |
237 """Returns local paths of input_api.AffectedFiles().""" | 238 """Returns local paths of input_api.AffectedFiles().""" |
238 return [af.LocalPath() for af in self.AffectedFiles(include_dirs)] | 239 return [af.LocalPath() for af in self.AffectedFiles(include_dirs)] |
239 | 240 |
240 def AbsoluteLocalPaths(self, include_dirs=False): | 241 def AbsoluteLocalPaths(self, include_dirs=False): |
241 """Returns absolute local paths of input_api.AffectedFiles().""" | 242 """Returns absolute local paths of input_api.AffectedFiles().""" |
242 return [af.AbsoluteLocalPath() for af in self.AffectedFiles(include_dirs)] | 243 return [af.AbsoluteLocalPath() for af in self.AffectedFiles(include_dirs)] |
243 | 244 |
244 def ServerPaths(self, include_dirs=False): | 245 def ServerPaths(self, include_dirs=False): |
(...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
541 a 3 tuple: | 542 a 3 tuple: |
542 the AffectedFile instance of the current file; | 543 the AffectedFile instance of the current file; |
543 integer line number (1-based); and | 544 integer line number (1-based); and |
544 the contents of the line as a string. | 545 the contents of the line as a string. |
545 """ | 546 """ |
546 return InputApi._RightHandSideLinesImpl( | 547 return InputApi._RightHandSideLinesImpl( |
547 filter(lambda x: x.IsTextFile(), | 548 filter(lambda x: x.IsTextFile(), |
548 self.AffectedFiles(include_deletes=False))) | 549 self.AffectedFiles(include_deletes=False))) |
549 | 550 |
550 | 551 |
551 def ListRelevantPresubmitFiles(files): | 552 def ListRelevantPresubmitFiles(files, root): |
552 """Finds all presubmit files that apply to a given set of source files. | 553 """Finds all presubmit files that apply to a given set of source files. |
553 | 554 |
554 Args: | 555 Args: |
555 files: An iterable container containing file paths. | 556 files: An iterable container containing file paths. |
| 557 root: Path where to stop searching. |
556 | 558 |
557 Return: | 559 Return: |
558 ['foo/blat/PRESUBMIT.py', 'mat/gat/PRESUBMIT.py'] | 560 List of absolute paths of the existing PRESUBMIT.py scripts. |
559 """ | 561 """ |
560 checked_dirs = {} # Keys are directory paths, values are ignored. | 562 entries = [] |
561 source_dirs = [os.path.dirname(f) for f in files] | 563 for f in files: |
562 presubmit_files = [] | 564 f = normpath(os.path.join(root, f)) |
563 for dir in source_dirs: | 565 while f: |
564 while (True): | 566 f = os.path.dirname(f) |
565 if dir in checked_dirs: | 567 if f in entries: |
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 ['', '.']: | |
574 break | 568 break |
575 else: | 569 entries.append(f) |
576 dir = os.path.dirname(dir) | 570 if f == root: |
577 return presubmit_files | 571 break |
| 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) |
578 | 575 |
579 | 576 |
580 class PresubmitExecuter(object): | 577 class PresubmitExecuter(object): |
581 def __init__(self, change_info, committing): | 578 def __init__(self, change_info, committing): |
582 """ | 579 """ |
583 Args: | 580 Args: |
584 change_info: The ChangeInfo object for the change. | 581 change_info: The ChangeInfo object for the change. |
585 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 582 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
586 """ | 583 """ |
587 # TODO(maruel): Determine the SCM. | 584 # TODO(maruel): Determine the SCM. |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
646 change_info: The ChangeInfo object for the change. | 643 change_info: The ChangeInfo object for the change. |
647 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 644 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
648 verbose: Prints debug info. | 645 verbose: Prints debug info. |
649 output_stream: A stream to write output from presubmit tests to. | 646 output_stream: A stream to write output from presubmit tests to. |
650 input_stream: A stream to read input from the user. | 647 input_stream: A stream to read input from the user. |
651 default_presubmit: A default presubmit script to execute in any case. | 648 default_presubmit: A default presubmit script to execute in any case. |
652 | 649 |
653 Return: | 650 Return: |
654 True if execution can continue, False if not. | 651 True if execution can continue, False if not. |
655 """ | 652 """ |
656 presubmit_files = ListRelevantPresubmitFiles(change_info.FileList()) | 653 checkout_root = gcl.GetRepositoryRoot() |
| 654 presubmit_files = ListRelevantPresubmitFiles(change_info.FileList(), |
| 655 checkout_root) |
657 if not presubmit_files and verbose: | 656 if not presubmit_files and verbose: |
658 output_stream.write("Warning, no presubmit.py found.\n") | 657 output_stream.write("Warning, no presubmit.py found.\n") |
659 results = [] | 658 results = [] |
660 executer = PresubmitExecuter(change_info, committing) | 659 executer = PresubmitExecuter(change_info, committing) |
661 if default_presubmit: | 660 if default_presubmit: |
662 if verbose: | 661 if verbose: |
663 output_stream.write("Running default presubmit script.\n") | 662 output_stream.write("Running default presubmit script.\n") |
664 results += executer.ExecPresubmitScript(default_presubmit, 'PRESUBMIT.py') | 663 fake_path = os.path.join(checkout_root, '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 |