| 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.3.2' | 9 __version__ = '1.3.2' |
| 10 | 10 |
| (...skipping 486 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 497 | 497 |
| 498 Instance members: | 498 Instance members: |
| 499 tags: Dictionnary of KEY=VALUE pairs found in the change description. | 499 tags: Dictionnary of KEY=VALUE pairs found in the change description. |
| 500 self.KEY: equivalent to tags['KEY'] | 500 self.KEY: equivalent to tags['KEY'] |
| 501 """ | 501 """ |
| 502 | 502 |
| 503 # Matches key/value (or "tag") lines in changelist descriptions. | 503 # Matches key/value (or "tag") lines in changelist descriptions. |
| 504 _tag_line_re = re.compile( | 504 _tag_line_re = re.compile( |
| 505 '^\s*(?P<key>[A-Z][A-Z_0-9]*)\s*=\s*(?P<value>.*?)\s*$') | 505 '^\s*(?P<key>[A-Z][A-Z_0-9]*)\s*=\s*(?P<value>.*?)\s*$') |
| 506 | 506 |
| 507 def __init__(self, change_info, repository_root=''): | 507 def __init__(self, change_info): |
| 508 # Do not keep a reference to the original change_info. | 508 # Do not keep a reference to the original change_info. |
| 509 self._name = change_info.name | 509 self._name = change_info.name |
| 510 self._full_description = change_info.description | 510 self._full_description = change_info.description |
| 511 self._repository_root = repository_root | 511 self._repository_root = change_info.GetLocalRoot() |
| 512 self.issue = change_info.issue | 512 self.issue = change_info.issue |
| 513 self.patchset = change_info.patchset | 513 self.patchset = change_info.patchset |
| 514 | 514 |
| 515 # From the description text, build up a dictionary of key/value pairs | 515 # From the description text, build up a dictionary of key/value pairs |
| 516 # plus the description minus all key/value or "tag" lines. | 516 # plus the description minus all key/value or "tag" lines. |
| 517 self._description_without_tags = [] | 517 self._description_without_tags = [] |
| 518 self.tags = {} | 518 self.tags = {} |
| 519 for line in change_info.description.splitlines(): | 519 for line in change_info.description.splitlines(): |
| 520 m = self._tag_line_re.match(line) | 520 m = self._tag_line_re.match(line) |
| 521 if m: | 521 if m: |
| 522 self.tags[m.group('key')] = m.group('value') | 522 self.tags[m.group('key')] = m.group('value') |
| 523 else: | 523 else: |
| 524 self._description_without_tags.append(line) | 524 self._description_without_tags.append(line) |
| 525 | 525 |
| 526 # Change back to text and remove whitespace at end. | 526 # Change back to text and remove whitespace at end. |
| 527 self._description_without_tags = '\n'.join(self._description_without_tags) | 527 self._description_without_tags = '\n'.join(self._description_without_tags) |
| 528 self._description_without_tags = self._description_without_tags.rstrip() | 528 self._description_without_tags = self._description_without_tags.rstrip() |
| 529 | 529 |
| 530 self._affected_files = [ | 530 self._affected_files = [ |
| 531 SvnAffectedFile(info[1], info[0].strip(), repository_root) | 531 SvnAffectedFile(info[1], info[0].strip(), self._repository_root) |
| 532 for info in change_info.files | 532 for info in change_info.GetFiles() |
| 533 ] | 533 ] |
| 534 | 534 |
| 535 def Name(self): | 535 def Name(self): |
| 536 """Returns the change name.""" | 536 """Returns the change name.""" |
| 537 return self._name | 537 return self._name |
| 538 | 538 |
| 539 def DescriptionText(self): | 539 def DescriptionText(self): |
| 540 """Returns the user-entered changelist description, minus tags. | 540 """Returns the user-entered changelist description, minus tags. |
| 541 | 541 |
| 542 Any line in the user-provided description starting with e.g. "FOO=" | 542 Any line in the user-provided description starting with e.g. "FOO=" |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 644 break | 644 break |
| 645 entries.sort() | 645 entries.sort() |
| 646 entries = map(lambda x: os.path.join(x, 'PRESUBMIT.py'), entries) | 646 entries = map(lambda x: os.path.join(x, 'PRESUBMIT.py'), entries) |
| 647 return filter(lambda x: os.path.isfile(x), entries) | 647 return filter(lambda x: os.path.isfile(x), entries) |
| 648 | 648 |
| 649 | 649 |
| 650 class PresubmitExecuter(object): | 650 class PresubmitExecuter(object): |
| 651 def __init__(self, change_info, committing): | 651 def __init__(self, change_info, committing): |
| 652 """ | 652 """ |
| 653 Args: | 653 Args: |
| 654 change_info: The ChangeInfo object for the change. | 654 change_info: The gcl.ChangeInfo object for the change. |
| 655 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 655 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 656 """ | 656 """ |
| 657 # TODO(maruel): Determine the SCM. | 657 # TODO(maruel): Determine the SCM. |
| 658 self.change = GclChange(change_info, gcl.GetRepositoryRoot()) | 658 self.change = GclChange(change_info) |
| 659 self.committing = committing | 659 self.committing = committing |
| 660 | 660 |
| 661 def ExecPresubmitScript(self, script_text, presubmit_path): | 661 def ExecPresubmitScript(self, script_text, presubmit_path): |
| 662 """Executes a single presubmit script. | 662 """Executes a single presubmit script. |
| 663 | 663 |
| 664 Args: | 664 Args: |
| 665 script_text: The text of the presubmit script. | 665 script_text: The text of the presubmit script. |
| 666 presubmit_path: The path to the presubmit file (this will be reported via | 666 presubmit_path: The path to the presubmit file (this will be reported via |
| 667 input_api.PresubmitLocalPath()). | 667 input_api.PresubmitLocalPath()). |
| 668 | 668 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 707 """Runs all presubmit checks that apply to the files in the change. | 707 """Runs all presubmit checks that apply to the files in the change. |
| 708 | 708 |
| 709 This finds all PRESUBMIT.py files in directories enclosing the files in the | 709 This finds all PRESUBMIT.py files in directories enclosing the files in the |
| 710 change (up to the repository root) and calls the relevant entrypoint function | 710 change (up to the repository root) and calls the relevant entrypoint function |
| 711 depending on whether the change is being committed or uploaded. | 711 depending on whether the change is being committed or uploaded. |
| 712 | 712 |
| 713 Prints errors, warnings and notifications. Prompts the user for warnings | 713 Prints errors, warnings and notifications. Prompts the user for warnings |
| 714 when needed. | 714 when needed. |
| 715 | 715 |
| 716 Args: | 716 Args: |
| 717 change_info: The ChangeInfo object for the change. | 717 change_info: The gcl.ChangeInfo object for the change. |
| 718 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 718 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 719 verbose: Prints debug info. | 719 verbose: Prints debug info. |
| 720 output_stream: A stream to write output from presubmit tests to. | 720 output_stream: A stream to write output from presubmit tests to. |
| 721 input_stream: A stream to read input from the user. | 721 input_stream: A stream to read input from the user. |
| 722 default_presubmit: A default presubmit script to execute in any case. | 722 default_presubmit: A default presubmit script to execute in any case. |
| 723 may_prompt: Enable (y/n) questions on warning or error. | 723 may_prompt: Enable (y/n) questions on warning or error. |
| 724 | 724 |
| 725 Return: | 725 Return: |
| 726 True if execution can continue, False if not. | 726 True if execution can continue, False if not. |
| 727 """ | 727 """ |
| 728 checkout_root = gcl.GetRepositoryRoot() | 728 presubmit_files = ListRelevantPresubmitFiles(change_info.GetFileNames(), |
| 729 presubmit_files = ListRelevantPresubmitFiles(change_info.FileList(), | 729 change_info.local_root) |
| 730 checkout_root) | |
| 731 if not presubmit_files and verbose: | 730 if not presubmit_files and verbose: |
| 732 output_stream.write("Warning, no presubmit.py found.\n") | 731 output_stream.write("Warning, no presubmit.py found.\n") |
| 733 results = [] | 732 results = [] |
| 734 executer = PresubmitExecuter(change_info, committing) | 733 executer = PresubmitExecuter(change_info, committing) |
| 735 if default_presubmit: | 734 if default_presubmit: |
| 736 if verbose: | 735 if verbose: |
| 737 output_stream.write("Running default presubmit script.\n") | 736 output_stream.write("Running default presubmit script.\n") |
| 738 fake_path = os.path.join(checkout_root, 'PRESUBMIT.py') | 737 fake_path = os.path.join(change_info.local_root, 'PRESUBMIT.py') |
| 739 results += executer.ExecPresubmitScript(default_presubmit, fake_path) | 738 results += executer.ExecPresubmitScript(default_presubmit, fake_path) |
| 740 for filename in presubmit_files: | 739 for filename in presubmit_files: |
| 741 filename = os.path.abspath(filename) | 740 filename = os.path.abspath(filename) |
| 742 if verbose: | 741 if verbose: |
| 743 output_stream.write("Running %s\n" % filename) | 742 output_stream.write("Running %s\n" % filename) |
| 744 # Accept CRLF presubmit script. | 743 # Accept CRLF presubmit script. |
| 745 presubmit_script = gcl.ReadFile(filename, 'rU') | 744 presubmit_script = gcl.ReadFile(filename, 'rU') |
| 746 results += executer.ExecPresubmitScript(presubmit_script, filename) | 745 results += executer.ExecPresubmitScript(presubmit_script, filename) |
| 747 | 746 |
| 748 errors = [] | 747 errors = [] |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 814 options.commit, | 813 options.commit, |
| 815 options.verbose, | 814 options.verbose, |
| 816 sys.stdout, | 815 sys.stdout, |
| 817 sys.stdin, | 816 sys.stdin, |
| 818 None, | 817 None, |
| 819 False) | 818 False) |
| 820 | 819 |
| 821 | 820 |
| 822 if __name__ == '__main__': | 821 if __name__ == '__main__': |
| 823 sys.exit(Main(sys.argv)) | 822 sys.exit(Main(sys.argv)) |
| OLD | NEW |