Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Side by Side Diff: presubmit_support.py

Issue 178223016: Support multiple try masters when sending tries to rietveld. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Addressed review comments. Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 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.8.0' 9 __version__ = '1.8.0'
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 cpplint 15 import cpplint
16 import cPickle # Exposed through the API. 16 import cPickle # Exposed through the API.
17 import cStringIO # Exposed through the API. 17 import cStringIO # Exposed through the API.
18 import contextlib 18 import contextlib
19 import fnmatch 19 import fnmatch
20 import glob 20 import glob
21 import inspect 21 import inspect
22 import itertools
22 import json # Exposed through the API. 23 import json # Exposed through the API.
23 import logging 24 import logging
24 import marshal # Exposed through the API. 25 import marshal # Exposed through the API.
25 import multiprocessing 26 import multiprocessing
26 import optparse 27 import optparse
27 import os # Somewhat exposed through the API. 28 import os # Somewhat exposed through the API.
28 import pickle # Exposed through the API. 29 import pickle # Exposed through the API.
29 import random 30 import random
30 import re # Exposed through the API. 31 import re # Exposed through the API.
31 import sys # Parts exposed through API. 32 import sys # Parts exposed through API.
(...skipping 968 matching lines...) Expand 10 before | Expand all | Expand 10 after
1000 results.append(p) 1001 results.append(p)
1001 1002
1002 logging.debug('Presubmit files: %s' % ','.join(results)) 1003 logging.debug('Presubmit files: %s' % ','.join(results))
1003 return results 1004 return results
1004 1005
1005 1006
1006 class GetTrySlavesExecuter(object): 1007 class GetTrySlavesExecuter(object):
1007 @staticmethod 1008 @staticmethod
1008 def ExecPresubmitScript(script_text, presubmit_path, project, change): 1009 def ExecPresubmitScript(script_text, presubmit_path, project, change):
1009 """Executes GetPreferredTrySlaves() from a single presubmit script. 1010 """Executes GetPreferredTrySlaves() from a single presubmit script.
1011
1012 This will soon be deprecated and replaced by GetPreferredTryMasters().
1010 1013
1011 Args: 1014 Args:
1012 script_text: The text of the presubmit script. 1015 script_text: The text of the presubmit script.
1013 presubmit_path: Project script to run. 1016 presubmit_path: Project script to run.
1014 project: Project name to pass to presubmit script for bot selection. 1017 project: Project name to pass to presubmit script for bot selection.
1015 1018
1016 Return: 1019 Return:
1017 A list of try slaves. 1020 A list of try slaves.
1018 """ 1021 """
1019 context = {} 1022 context = {}
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
1067 ) 1070 )
1068 1071
1069 # Ensure it's either all old-style or all new-style. 1072 # Ensure it's either all old-style or all new-style.
1070 if not valid_oldstyle(result) and not valid_newstyle(result): 1073 if not valid_oldstyle(result) and not valid_newstyle(result):
1071 raise PresubmitFailure( 1074 raise PresubmitFailure(
1072 'PRESUBMIT.py returned invalid trybot specification!') 1075 'PRESUBMIT.py returned invalid trybot specification!')
1073 1076
1074 return result 1077 return result
1075 1078
1076 1079
1080 class GetTryMastersExecuter(object):
1081 @staticmethod
1082 def ExecPresubmitScript(script_text, presubmit_path, project, change):
1083 """Executes GetPreferredTryMasters() from a single presubmit script.
1084
1085 Args:
1086 script_text: The text of the presubmit script.
1087 presubmit_path: Project script to run.
1088 project: Project name to pass to presubmit script for bot selection.
1089
1090 Return:
1091 A map of try masters to map of builders to set of tests.
1092 """
1093 context = {}
1094 try:
1095 exec script_text in context
ghost stip (do not use) 2014/03/01 00:56:16 if possible, try to refactor this out to share cod
Michael Achenbach 2014/03/01 01:30:40 Follow up CL.
1096 except Exception, e:
1097 raise PresubmitFailure('"%s" had an exception.\n%s'
1098 % (presubmit_path, e))
1099
1100 function_name = 'GetPreferredTryMasters'
1101 if function_name not in context:
1102 return {}
1103 get_preferred_try_masters = context[function_name]
1104 if not len(inspect.getargspec(get_preferred_try_masters)[0]) == 2:
1105 raise PresubmitFailure(
1106 'Expected function "GetPreferredTryMasters" to take two arguments.')
1107 result = get_preferred_try_masters(project, change)
1108
1109 def CheckType(item, t):
ghost stip (do not use) 2014/03/01 00:56:16 I'm ok removing lines 1109 to 1141, since any code
Michael Achenbach 2014/03/01 01:30:40 Done.
1110 if not isinstance(item, t):
1111 raise PresubmitFailure(
1112 'Expected a %s, got a %s instead: %s' %
1113 (str(t), type(item), str(item)))
1114
1115 def CheckString(item):
1116 if not item:
1117 raise PresubmitFailure(
1118 'PRESUBMIT.py returned empty trymaster/builder string.')
1119 CheckType(item, basestring)
1120 if item != item.strip():
1121 raise PresubmitFailure(
1122 'Try master/builder names cannot start/end with whitespace')
1123 if ',' in item:
1124 raise PresubmitFailure(
1125 'Do not use \',\' separated master, builder or test names: %s'
1126 % item)
1127
1128 CheckType(result, dict)
1129 for (master, builders) in result.iteritems():
1130 CheckString(master)
1131 CheckType(builders, dict)
1132 for (builder, tests) in builders.iteritems():
1133 CheckString(builder)
1134 CheckType(tests, set)
1135 if not tests:
1136 raise PresubmitFailure(
1137 'PRESUBMIT.py returned empty tests for builder %s. '
1138 'Maybe specify "defaulttests"?' % builder)
1139 return result
1140
1141
1077 def DoGetTrySlaves(change, 1142 def DoGetTrySlaves(change,
1078 changed_files, 1143 changed_files,
1079 repository_root, 1144 repository_root,
1080 default_presubmit, 1145 default_presubmit,
1081 project, 1146 project,
1082 verbose, 1147 verbose,
1083 output_stream): 1148 output_stream):
1084 """Get the list of try servers from the presubmit scripts. 1149 """Get the list of try servers from the presubmit scripts (deprecated).
1085 1150
1086 Args: 1151 Args:
1087 changed_files: List of modified files. 1152 changed_files: List of modified files.
1088 repository_root: The repository root. 1153 repository_root: The repository root.
1089 default_presubmit: A default presubmit script to execute in any case. 1154 default_presubmit: A default presubmit script to execute in any case.
1090 project: Optional name of a project used in selecting trybots. 1155 project: Optional name of a project used in selecting trybots.
1091 verbose: Prints debug info. 1156 verbose: Prints debug info.
1092 output_stream: A stream to write debug output to. 1157 output_stream: A stream to write debug output to.
1093 1158
1094 Return: 1159 Return:
(...skipping 30 matching lines...) Expand all
1125 slaves = list(slave_dict.items()) 1190 slaves = list(slave_dict.items())
1126 1191
1127 slaves.extend(set(old_style)) 1192 slaves.extend(set(old_style))
1128 1193
1129 if slaves and verbose: 1194 if slaves and verbose:
1130 output_stream.write(', '.join((str(x) for x in slaves))) 1195 output_stream.write(', '.join((str(x) for x in slaves)))
1131 output_stream.write('\n') 1196 output_stream.write('\n')
1132 return slaves 1197 return slaves
1133 1198
1134 1199
1200 def _MergeMasters(masters1, masters2):
1201 """Merges two master maps. Merges also the tests of each builder.
1202 """
ghost stip (do not use) 2014/03/01 00:56:16 nit: keep """ on same line
1203 result = {}
1204 for (master, builders) in itertools.chain(masters1.iteritems(),
1205 masters2.iteritems()):
1206 new_builders = result.setdefault(master, {})
1207 for (builder, tests) in builders.iteritems():
1208 new_builders.setdefault(builder, set([])).update(tests)
1209 return result
1210
1211
1212 def DoGetTryMasters(change,
1213 changed_files,
1214 repository_root,
1215 default_presubmit,
1216 project,
1217 verbose,
1218 output_stream):
1219 """Get the list of try masters from the presubmit scripts.
1220
1221 Args:
1222 changed_files: List of modified files.
1223 repository_root: The repository root.
1224 default_presubmit: A default presubmit script to execute in any case.
1225 project: Optional name of a project used in selecting trybots.
1226 verbose: Prints debug info.
1227 output_stream: A stream to write debug output to.
1228
1229 Return:
1230 Map of try masters to map of builders to set of tests.
1231 """
1232 presubmit_files = ListRelevantPresubmitFiles(changed_files, repository_root)
1233 if not presubmit_files and verbose:
1234 output_stream.write("Warning, no presubmit.py found.\n")
ghost stip (do not use) 2014/03/01 00:56:16 nit: PRESUBMIT.py vs presubmit.py
Michael Achenbach 2014/03/01 01:30:40 Done.
1235 results = {}
1236 executer = GetTryMastersExecuter()
1237
1238 if default_presubmit:
1239 if verbose:
1240 output_stream.write("Running default presubmit script.\n")
1241 fake_path = os.path.join(repository_root, 'PRESUBMIT.py')
1242 results = _MergeMasters(results, executer.ExecPresubmitScript(
1243 default_presubmit, fake_path, project, change))
1244 for filename in presubmit_files:
1245 filename = os.path.abspath(filename)
1246 if verbose:
1247 output_stream.write("Running %s\n" % filename)
1248 # Accept CRLF presubmit script.
1249 presubmit_script = gclient_utils.FileRead(filename, 'rU')
1250 results = _MergeMasters(results, executer.ExecPresubmitScript(
1251 presubmit_script, filename, project, change))
1252
1253 # Make sets to lists again for later JSON serialization.
1254 for (_, builders) in results.iteritems():
ghost stip (do not use) 2014/03/01 00:56:16 for builders in results.itervalues()
Michael Achenbach 2014/03/01 01:30:40 Done.
1255 for builder in builders:
1256 builders[builder] = list(builders[builder])
1257
1258 if results and verbose:
1259 output_stream.write('%s\n' % str(results))
1260 return results
1261
1262
1135 class PresubmitExecuter(object): 1263 class PresubmitExecuter(object):
1136 def __init__(self, change, committing, rietveld_obj, verbose): 1264 def __init__(self, change, committing, rietveld_obj, verbose):
1137 """ 1265 """
1138 Args: 1266 Args:
1139 change: The Change object. 1267 change: The Change object.
1140 committing: True if 'gcl commit' is running, False if 'gcl upload' is. 1268 committing: True if 'gcl commit' is running, False if 'gcl upload' is.
1141 rietveld_obj: rietveld.Rietveld client object. 1269 rietveld_obj: rietveld.Rietveld client object.
1142 """ 1270 """
1143 self.change = change 1271 self.change = change
1144 self.committing = committing 1272 self.committing = committing
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after
1508 except PresubmitFailure, e: 1636 except PresubmitFailure, e:
1509 print >> sys.stderr, e 1637 print >> sys.stderr, e
1510 print >> sys.stderr, 'Maybe your depot_tools is out of date?' 1638 print >> sys.stderr, 'Maybe your depot_tools is out of date?'
1511 print >> sys.stderr, 'If all fails, contact maruel@' 1639 print >> sys.stderr, 'If all fails, contact maruel@'
1512 return 2 1640 return 2
1513 1641
1514 1642
1515 if __name__ == '__main__': 1643 if __name__ == '__main__':
1516 fix_encoding.fix_encoding() 1644 fix_encoding.fix_encoding()
1517 sys.exit(Main(None)) 1645 sys.exit(Main(None))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698