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

Side by Side Diff: scripts/slave/annotated_run.py

Issue 14988009: First cut of testing infrastructure for recipes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Pylinting Created 7 years, 7 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) 2013 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2013 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 """Entry point for fully-annotated builds. 6 """Entry point for fully-annotated builds.
7 7
8 This script is part of the effort to move all builds to annotator-based 8 This script is part of the effort to move all builds to annotator-based
9 systems. Any builder configured to use the AnnotatorFactory.BaseFactory() 9 systems. Any builder configured to use the AnnotatorFactory.BaseFactory()
10 found in scripts/master/factory/annotator_factory.py executes a single 10 found in scripts/master/factory/annotator_factory.py executes a single
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
42 """ 42 """
43 43
44 import contextlib 44 import contextlib
45 import json 45 import json
46 import optparse 46 import optparse
47 import os 47 import os
48 import subprocess 48 import subprocess
49 import sys 49 import sys
50 import tempfile 50 import tempfile
51 51
52 from collections import namedtuple
53
52 from common import annotator 54 from common import annotator
53 from common import chromium_utils 55 from common import chromium_utils
54 from slave import recipe_util 56 from slave import recipe_util
55 from slave import annotated_checkout 57 from slave import annotated_checkout
56 58
57 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) 59 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
60 BUILD_ROOT = os.path.dirname(os.path.dirname(SCRIPT_PATH))
58 61
59 62
60 @contextlib.contextmanager 63 @contextlib.contextmanager
61 def temp_purge_path(path): 64 def temp_purge_path(path):
62 saved = sys.path 65 saved = sys.path
63 sys.path = [path] 66 sys.path = [path]
64 try: 67 try:
65 yield 68 yield
66 finally: 69 finally:
67 sys.path = saved 70 sys.path = saved
68 71
69 72
70 def expand_root_placeholder(root, lst): 73 def expand_root_placeholder(root, lst):
71 """This expands CheckoutRootPlaceholder in paths to a real path. 74 """This expands CheckoutRootPlaceholder in paths to a real path.
72 See recipe_util.checkout_path() for usage.""" 75 See recipe_util.checkout_path() for usage."""
73 ret = [] 76 ret = []
74 replacements = {'CheckoutRootPlaceholder': root} 77 replacements = {'CheckoutRootPlaceholder': root}
75 for item in lst: 78 for item in lst:
76 if isinstance(item, str): 79 if isinstance(item, str):
77 if '%(CheckoutRootPlaceholder)s' in item: 80 if '%(CheckoutRootPlaceholder)s' in item:
78 assert root, 'Must use "checkout" key to use checkout_path().' 81 assert root, 'Must use "checkout" key to use checkout_path().'
79 ret.append(item % replacements) 82 ret.append(item % replacements)
80 continue 83 continue
81 ret.append(item) 84 ret.append(item)
82 return ret 85 return ret
83 86
84 87
85 def get_args(): 88 def get_args(argv):
86 """Process command-line arguments.""" 89 """Process command-line arguments."""
87 90
88 parser = optparse.OptionParser( 91 parser = optparse.OptionParser(
89 description='Entry point for annotated builds.') 92 description='Entry point for annotated builds.')
90 parser.add_option('--build-properties', 93 parser.add_option('--build-properties',
91 action='callback', callback=chromium_utils.convert_json, 94 action='callback', callback=chromium_utils.convert_json,
92 type='string', default={}, 95 type='string', default={},
93 help='build properties in JSON format') 96 help='build properties in JSON format')
94 parser.add_option('--factory-properties', 97 parser.add_option('--factory-properties',
95 action='callback', callback=chromium_utils.convert_json, 98 action='callback', callback=chromium_utils.convert_json,
96 type='string', default={}, 99 type='string', default={},
97 help='factory properties in JSON format') 100 help='factory properties in JSON format')
98 parser.add_option('--output-build-properties', action='store_true',
99 help='output JSON-encoded build properties extracted from'
100 ' the build')
101 parser.add_option('--output-factory-properties', action='store_true',
102 help='output JSON-encoded factory properties extracted from'
103 'the build factory')
104 parser.add_option('--keep-stdin', action='store_true', default=False, 101 parser.add_option('--keep-stdin', action='store_true', default=False,
105 help='don\'t close stdin when running recipe steps') 102 help='don\'t close stdin when running recipe steps')
106 return parser.parse_args() 103 return parser.parse_args(argv)
107 104
108 105
109 def main(): 106 def main(argv=None):
110 opts, _ = get_args() 107 opts, _ = get_args(argv)
108
109 stream = annotator.StructuredAnnotationStream(seed_steps=['setup_build'])
110
111 ret = make_steps(stream, opts.build_properties, opts.factory_properties)
112 assert ret.script is None, "Unexpectedly got script from make_steps?"
Mike Stip (use stip instead) 2013/05/14 18:01:32 can you put a comment here saying that script is m
iannucci 2013/05/14 19:18:14 Good point, done.
113
114 if ret.status_code:
115 return ret
116 else:
117 return run_annotator(stream, ret.steps, opts.keep_stdin)
118
119 def make_steps(stream, build_properties, factory_properties,
120 test_mode=False):
121 """Returns a namedtuple of (status_code, script, steps).
122
123 Only one of these values will be set at a time.
124 """
125 MakeStepsRetval = namedtuple('MakeStepsRetval', 'status_code script steps')
Mike Stip (use stip instead) 2013/05/14 18:01:32 didn't know about namedtuple. cool!
iannucci 2013/05/14 19:18:14 :)
111 126
112 # TODO(iannucci): Stop this when blamelist becomes sane data. 127 # TODO(iannucci): Stop this when blamelist becomes sane data.
113 if ('blamelist_real' in opts.build_properties and 128 if ('blamelist_real' in build_properties and
114 'blamelist' in opts.build_properties): 129 'blamelist' in build_properties):
115 opts.build_properties['blamelist'] = opts.build_properties['blamelist_real'] 130 build_properties['blamelist'] = build_properties['blamelist_real']
116 del opts.build_properties['blamelist_real'] 131 del build_properties['blamelist_real']
117 132
118 # Supplement the master-supplied factory_properties dictionary with the values
119 # found in the slave-side recipe.
120 stream = annotator.StructuredAnnotationStream(seed_steps=['setup_build'])
121 with stream.step('setup_build') as s: 133 with stream.step('setup_build') as s:
122 assert 'recipe' in opts.factory_properties 134 assert 'recipe' in factory_properties
123 factory_properties = opts.factory_properties
124 recipe = factory_properties['recipe'] 135 recipe = factory_properties['recipe']
125 recipe_dirs = (os.path.abspath(p) for p in ( 136 recipe_dirs = (os.path.abspath(p) for p in (
126 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', 137 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts',
127 'slave-internal', 'recipes'), 138 'slave-internal', 'recipes'),
128 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', 139 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts',
129 'slave', 'recipes'), 140 'slave', 'recipes'),
130 os.path.join(SCRIPT_PATH, 'recipes'), 141 os.path.join(SCRIPT_PATH, 'recipes'),
131 )) 142 ))
132 143
133 for path in recipe_dirs: 144 for path in recipe_dirs:
134 recipe_module = None 145 recipe_module = None
135 with temp_purge_path(path): 146 with temp_purge_path(path):
136 try: 147 try:
137 recipe_module = __import__(recipe, globals(), locals()) 148 recipe_module = __import__(recipe, globals(), locals())
138 except ImportError: 149 except ImportError:
139 continue 150 continue
140 recipe_dict = recipe_module.GetFactoryProperties( 151 recipe_dict = recipe_module.GetFactoryProperties(
141 recipe_util, 152 recipe_util,
142 opts.factory_properties.copy(), 153 factory_properties.copy(),
143 opts.build_properties.copy()) 154 build_properties.copy())
144 break 155 break
145 else: 156 else:
146 s.step_text('recipe not found') 157 s.step_text('recipe not found')
147 s.step_failure() 158 s.step_failure()
148 return 1 159 return MakeStepsRetval(1, None, None)
149 160
150 factory_properties.update(recipe_dict) 161 factory_properties.update(recipe_dict)
151 162
152 # If a checkout is specified, get its type and spec and pass them 163 # If a checkout is specified, get its type and spec and pass them
153 # off to annotated_checkout.py to actually fetch the repo. 164 # off to annotated_checkout.py to actually fetch the repo.
154 # annotated_checkout.py handles its own StructuredAnnotationStream. 165 # annotated_checkout.py handles its own StructuredAnnotationStream.
155 root = None 166 root = None
156 if 'checkout' in factory_properties: 167 if 'checkout' in factory_properties:
157 checkout_type = factory_properties['checkout'] 168 checkout_type = factory_properties['checkout']
158 checkout_spec = factory_properties['%s_spec' % checkout_type] 169 checkout_spec = factory_properties['%s_spec' % checkout_type]
159 ret, root = annotated_checkout.run(checkout_type, checkout_spec) 170 ret, root = annotated_checkout.run(checkout_type, checkout_spec,
171 test_mode)
160 if ret != 0: 172 if ret != 0:
161 return ret 173 return MakeStepsRetval(ret, None, None)
174 if test_mode:
175 root = '[BUILD_ROOT]'+root[len(BUILD_ROOT):]
162 176
163 assert ('script' in factory_properties) ^ ('steps' in factory_properties) 177 assert ('script' in factory_properties) ^ ('steps' in factory_properties)
164 ret = 0 178 ret = 0
165 179
166 # If a script is specified, import it, execute its GetSteps method, 180 # If a script is specified, import it, execute its GetSteps method,
167 # and pass those steps forward so they get executed by annotator.py. 181 # and pass those steps forward so they get executed by annotator.py.
182 # If we're in test_mode mode, just return the script.
168 if 'script' in factory_properties: 183 if 'script' in factory_properties:
169 with stream.step('get_steps') as s: 184 with stream.step('get_steps') as s:
170 assert isinstance(factory_properties['script'], str) 185 assert isinstance(factory_properties['script'], str)
171 [script] = expand_root_placeholder(root, [factory_properties['script']]) 186 [script] = expand_root_placeholder(root, [factory_properties['script']])
187 if test_mode:
188 return MakeStepsRetval(None, script, None)
172 assert os.path.abspath(script) == script 189 assert os.path.abspath(script) == script
190
173 with temp_purge_path(os.path.dirname(script)): 191 with temp_purge_path(os.path.dirname(script)):
174 try: 192 try:
175 script_name = os.path.splitext(os.path.basename(script))[0] 193 script_name = os.path.splitext(os.path.basename(script))[0]
176 script_module = __import__(script_name, globals(), locals()) 194 script_module = __import__(script_name, globals(), locals())
177 except ImportError: 195 except ImportError:
178 s.step_text('script not found') 196 s.step_text('script not found')
179 s.step_failure() 197 s.step_failure()
180 return 1 198 return MakeStepsRetval(1, None, None)
181 steps_dict = script_module.GetSteps(recipe_util, 199 steps_dict = script_module.GetSteps(recipe_util,
182 opts.factory_properties.copy(), 200 factory_properties.copy(),
183 opts.build_properties.copy()) 201 build_properties.copy())
184 factory_properties['steps'] = steps_dict 202 factory_properties['steps'] = steps_dict
185 203
186 # Execute annotator.py with steps if specified. 204 # Execute annotator.py with steps if specified.
187 # annotator.py handles the seeding, execution, and annotation of each step. 205 # annotator.py handles the seeding, execution, and annotation of each step.
188 if 'steps' in factory_properties: 206 if 'steps' in factory_properties:
189 steps = factory_properties.pop('steps') 207 steps = factory_properties.pop('steps')
190 factory_properties_str = json.dumps(factory_properties) 208 factory_properties_str = json.dumps(factory_properties)
191 build_properties_str = json.dumps(opts.build_properties) 209 build_properties_str = json.dumps(build_properties)
192 property_placeholder_lst = [ 210 property_placeholder_lst = [
193 '--factory-properties', factory_properties_str, 211 '--factory-properties', factory_properties_str,
194 '--build-properties', build_properties_str] 212 '--build-properties', build_properties_str]
195 for step in steps: 213 for step in steps:
196 new_cmd = [] 214 new_cmd = []
197 for item in expand_root_placeholder(root, step['cmd']): 215 for item in expand_root_placeholder(root, step['cmd']):
198 if item == recipe_util.PropertyPlaceholder: 216 if item == recipe_util.PropertyPlaceholder:
199 new_cmd.extend(property_placeholder_lst) 217 new_cmd.extend(property_placeholder_lst)
200 else: 218 else:
201 new_cmd.append(item) 219 new_cmd.append(item)
202 step['cmd'] = new_cmd 220 step['cmd'] = new_cmd
203 if 'cwd' in step: 221 if 'cwd' in step:
204 [new_cwd] = expand_root_placeholder(root, [step['cwd']]) 222 [new_cwd] = expand_root_placeholder(root, [step['cwd']])
205 step['cwd'] = new_cwd 223 step['cwd'] = new_cwd
206 annotator_path = os.path.join( 224
207 os.path.dirname(SCRIPT_PATH), 'common', 'annotator.py') 225 return MakeStepsRetval(None, None, steps)
208 tmpfile, tmpname = tempfile.mkstemp() 226
209 try: 227 def run_annotator(stream, steps, keep_stdin):
210 cmd = [sys.executable, annotator_path, tmpname] 228 ret = 0
211 step_doc = json.dumps(steps) 229 annotator_path = os.path.join(
212 with os.fdopen(tmpfile, 'wb') as f: 230 os.path.dirname(SCRIPT_PATH), 'common', 'annotator.py')
213 f.write(step_doc) 231 tmpfile, tmpname = tempfile.mkstemp()
214 with stream.step('annotator_preamble') as s: 232 try:
215 print 'in %s executing: %s' % (os.getcwd(), ' '.join(cmd)) 233 cmd = [sys.executable, annotator_path, tmpname]
216 print 'with: %s' % step_doc 234 step_doc = json.dumps(steps)
217 if opts.keep_stdin: 235 with os.fdopen(tmpfile, 'wb') as f:
218 ret = subprocess.call(cmd) 236 f.write(step_doc)
219 else: 237 with stream.step('annotator_preamble'):
220 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE) 238 print 'in %s executing: %s' % (os.getcwd(), ' '.join(cmd))
221 proc.communicate('') 239 print 'with: %s' % step_doc
222 ret = proc.returncode 240 if keep_stdin:
223 finally: 241 ret = subprocess.call(cmd)
224 os.unlink(tmpname) 242 else:
243 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
244 proc.communicate('')
245 ret = proc.returncode
246 finally:
247 os.unlink(tmpname)
225 248
226 return ret 249 return ret
227 250
228 251
229 def UpdateScripts(): 252 def UpdateScripts():
230 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'): 253 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'):
231 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS') 254 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS')
232 return False 255 return False
233 stream = annotator.StructuredAnnotationStream(seed_steps=['update_scripts']) 256 stream = annotator.StructuredAnnotationStream(seed_steps=['update_scripts'])
234 with stream.step('update_scripts') as s: 257 with stream.step('update_scripts') as s:
235 build_root = os.path.join(SCRIPT_PATH, '..', '..') 258 build_root = os.path.join(SCRIPT_PATH, '..', '..')
236 gclient_name = 'gclient' 259 gclient_name = 'gclient'
237 if sys.platform.startswith('win'): 260 if sys.platform.startswith('win'):
238 gclient_name += '.bat' 261 gclient_name += '.bat'
239 gclient_path = os.path.join(build_root, '..', 'depot_tools', gclient_name) 262 gclient_path = os.path.join(build_root, '..', 'depot_tools', gclient_name)
240 if subprocess.call([gclient_path, 'sync', '--force'], cwd=build_root) != 0: 263 if subprocess.call([gclient_path, 'sync', '--force'], cwd=build_root) != 0:
241 s.step_text('gclient sync failed!') 264 s.step_text('gclient sync failed!')
242 s.step_warnings() 265 s.step_warnings()
243 os.environ['RUN_SLAVE_UPDATED_SCRIPTS'] = '1' 266 os.environ['RUN_SLAVE_UPDATED_SCRIPTS'] = '1'
244 return True 267 return True
245 268
246 269
247 if __name__ == '__main__': 270 if __name__ == '__main__':
248 if UpdateScripts(): 271 if UpdateScripts():
249 os.execv(sys.executable, [sys.executable] + sys.argv) 272 os.execv(sys.executable, [sys.executable] + sys.argv)
250 sys.exit(main()) 273 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698