OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 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.6.1' | 9 __version__ = '1.6.1' |
10 | 10 |
11 # TODO(joi) Add caching where appropriate/needed. The API is designed to allow | 11 # TODO(joi) Add caching where appropriate/needed. The API is designed to allow |
12 # caching (between all different invocations of presubmit scripts for a given | 12 # caching (between all different invocations of presubmit scripts for a given |
13 # change). We should add it as our presubmit scripts start feeling slow. | 13 # change). We should add it as our presubmit scripts start feeling slow. |
14 | 14 |
15 import cPickle # Exposed through the API. | 15 import cPickle # Exposed through the API. |
16 import cStringIO # Exposed through the API. | 16 import cStringIO # Exposed through the API. |
17 import fnmatch | 17 import fnmatch |
18 import glob | 18 import glob |
19 import inspect | |
20 import logging | 19 import logging |
21 import marshal # Exposed through the API. | 20 import marshal # Exposed through the API. |
22 import optparse | 21 import optparse |
23 import os # Somewhat exposed through the API. | 22 import os # Somewhat exposed through the API. |
24 import pickle # Exposed through the API. | 23 import pickle # Exposed through the API. |
25 import random | 24 import random |
26 import re # Exposed through the API. | 25 import re # Exposed through the API. |
27 import sys # Parts exposed through API. | 26 import sys # Parts exposed through API. |
28 import tempfile # Exposed through the API. | 27 import tempfile # Exposed through the API. |
29 import time | 28 import time |
(...skipping 839 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
869 p = os.path.join(directory, 'PRESUBMIT.py') | 868 p = os.path.join(directory, 'PRESUBMIT.py') |
870 if os.path.isfile(p): | 869 if os.path.isfile(p): |
871 results.append(p) | 870 results.append(p) |
872 | 871 |
873 logging.debug('Presubmit files: %s' % ','.join(results)) | 872 logging.debug('Presubmit files: %s' % ','.join(results)) |
874 return results | 873 return results |
875 | 874 |
876 | 875 |
877 class GetTrySlavesExecuter(object): | 876 class GetTrySlavesExecuter(object): |
878 @staticmethod | 877 @staticmethod |
879 def ExecPresubmitScript(script_text, presubmit_path, project, change): | 878 def ExecPresubmitScript(script_text, presubmit_path, project): |
880 """Executes GetPreferredTrySlaves() from a single presubmit script. | 879 """Executes GetPreferredTrySlaves() from a single presubmit script. |
881 | 880 |
882 Args: | 881 Args: |
883 script_text: The text of the presubmit script. | 882 script_text: The text of the presubmit script. |
884 presubmit_path: Project script to run. | 883 presubmit_path: Project script to run. |
885 project: Project name to pass to presubmit script for bot selection. | 884 project: Project name to pass to presubmit script for bot selection. |
886 | 885 |
887 Return: | 886 Return: |
888 A list of try slaves. | 887 A list of try slaves. |
889 """ | 888 """ |
890 context = {} | 889 context = {} |
891 try: | 890 try: |
892 exec script_text in context | 891 exec script_text in context |
893 except Exception, e: | 892 except Exception, e: |
894 raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e)) | 893 raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e)) |
895 | 894 |
896 function_name = 'GetPreferredTrySlaves' | 895 function_name = 'GetPreferredTrySlaves' |
897 if function_name in context: | 896 if function_name in context: |
898 get_preferred_try_slaves = context[function_name] | 897 try: |
899 function_info = inspect.getargspec(get_preferred_try_slaves) | 898 result = eval(function_name + '(' + repr(project) + ')', context) |
900 if len(function_info[0]) == 1: | 899 except TypeError: |
901 result = get_preferred_try_slaves(project) | 900 result = eval(function_name + '()', context) |
902 elif len(function_info[0]) == 2: | |
903 result = get_preferred_try_slaves(project, change) | |
904 else: | |
905 result = get_preferred_try_slaves() | |
906 if not isinstance(result, types.ListType): | 901 if not isinstance(result, types.ListType): |
907 raise PresubmitFailure( | 902 raise PresubmitFailure( |
908 'Presubmit functions must return a list, got a %s instead: %s' % | 903 'Presubmit functions must return a list, got a %s instead: %s' % |
909 (type(result), str(result))) | 904 (type(result), str(result))) |
910 for item in result: | 905 for item in result: |
911 if not isinstance(item, basestring): | 906 if not isinstance(item, basestring): |
912 raise PresubmitFailure('All try slaves names must be strings.') | 907 raise PresubmitFailure('All try slaves names must be strings.') |
913 if item != item.strip(): | 908 if item != item.strip(): |
914 raise PresubmitFailure( | 909 raise PresubmitFailure( |
915 'Try slave names cannot start/end with whitespace') | 910 'Try slave names cannot start/end with whitespace') |
916 else: | 911 else: |
917 result = [] | 912 result = [] |
918 return result | 913 return result |
919 | 914 |
920 | 915 |
921 def DoGetTrySlaves(change, | 916 def DoGetTrySlaves(changed_files, |
922 changed_files, | |
923 repository_root, | 917 repository_root, |
924 default_presubmit, | 918 default_presubmit, |
925 project, | 919 project, |
926 verbose, | 920 verbose, |
927 output_stream): | 921 output_stream): |
928 """Get the list of try servers from the presubmit scripts. | 922 """Get the list of try servers from the presubmit scripts. |
929 | 923 |
930 Args: | 924 Args: |
931 changed_files: List of modified files. | 925 changed_files: List of modified files. |
932 repository_root: The repository root. | 926 repository_root: The repository root. |
933 default_presubmit: A default presubmit script to execute in any case. | 927 default_presubmit: A default presubmit script to execute in any case. |
934 project: Optional name of a project used in selecting trybots. | 928 project: Optional name of a project used in selecting trybots. |
935 verbose: Prints debug info. | 929 verbose: Prints debug info. |
936 output_stream: A stream to write debug output to. | 930 output_stream: A stream to write debug output to. |
937 | 931 |
938 Return: | 932 Return: |
939 List of try slaves | 933 List of try slaves |
940 """ | 934 """ |
941 presubmit_files = ListRelevantPresubmitFiles(changed_files, repository_root) | 935 presubmit_files = ListRelevantPresubmitFiles(changed_files, repository_root) |
942 if not presubmit_files and verbose: | 936 if not presubmit_files and verbose: |
943 output_stream.write("Warning, no presubmit.py found.\n") | 937 output_stream.write("Warning, no presubmit.py found.\n") |
944 results = [] | 938 results = [] |
945 executer = GetTrySlavesExecuter() | 939 executer = GetTrySlavesExecuter() |
946 if default_presubmit: | 940 if default_presubmit: |
947 if verbose: | 941 if verbose: |
948 output_stream.write("Running default presubmit script.\n") | 942 output_stream.write("Running default presubmit script.\n") |
949 fake_path = os.path.join(repository_root, 'PRESUBMIT.py') | 943 fake_path = os.path.join(repository_root, 'PRESUBMIT.py') |
950 results += executer.ExecPresubmitScript( | 944 results += executer.ExecPresubmitScript( |
951 default_presubmit, fake_path, project, change) | 945 default_presubmit, fake_path, project) |
952 for filename in presubmit_files: | 946 for filename in presubmit_files: |
953 filename = os.path.abspath(filename) | 947 filename = os.path.abspath(filename) |
954 if verbose: | 948 if verbose: |
955 output_stream.write("Running %s\n" % filename) | 949 output_stream.write("Running %s\n" % filename) |
956 # Accept CRLF presubmit script. | 950 # Accept CRLF presubmit script. |
957 presubmit_script = gclient_utils.FileRead(filename, 'rU') | 951 presubmit_script = gclient_utils.FileRead(filename, 'rU') |
958 results += executer.ExecPresubmitScript( | 952 results += executer.ExecPresubmitScript( |
959 presubmit_script, filename, project, change) | 953 presubmit_script, filename, project) |
960 | 954 |
961 slaves = list(set(results)) | 955 slaves = list(set(results)) |
962 if slaves and verbose: | 956 if slaves and verbose: |
963 output_stream.write(', '.join(slaves)) | 957 output_stream.write(', '.join(slaves)) |
964 output_stream.write('\n') | 958 output_stream.write('\n') |
965 return slaves | 959 return slaves |
966 | 960 |
967 | 961 |
968 class PresubmitExecuter(object): | 962 class PresubmitExecuter(object): |
969 def __init__(self, change, committing, rietveld_obj, verbose): | 963 def __init__(self, change, committing, rietveld_obj, verbose): |
(...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1252 except PresubmitFailure, e: | 1246 except PresubmitFailure, e: |
1253 print >> sys.stderr, e | 1247 print >> sys.stderr, e |
1254 print >> sys.stderr, 'Maybe your depot_tools is out of date?' | 1248 print >> sys.stderr, 'Maybe your depot_tools is out of date?' |
1255 print >> sys.stderr, 'If all fails, contact maruel@' | 1249 print >> sys.stderr, 'If all fails, contact maruel@' |
1256 return 2 | 1250 return 2 |
1257 | 1251 |
1258 | 1252 |
1259 if __name__ == '__main__': | 1253 if __name__ == '__main__': |
1260 fix_encoding.fix_encoding() | 1254 fix_encoding.fix_encoding() |
1261 sys.exit(Main(None)) | 1255 sys.exit(Main(None)) |
OLD | NEW |