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

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: Add coverage to third_party. The C module is a speed-only optimization. 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 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
48 import subprocess 48 import subprocess
49 import sys 49 import sys
50 import tempfile 50 import tempfile
51 51
52 from common import annotator 52 from common import annotator
53 from common import chromium_utils 53 from common import chromium_utils
54 from slave import recipe_util 54 from slave import recipe_util
55 from slave import annotated_checkout 55 from slave import annotated_checkout
56 56
57 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) 57 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
58 BUILD_ROOT = os.path.dirname(os.path.dirname(SCRIPT_PATH))
58 59
59 60
60 @contextlib.contextmanager 61 @contextlib.contextmanager
61 def temp_purge_path(path): 62 def temp_purge_path(path):
62 saved = sys.path 63 saved = sys.path
63 sys.path = [path] 64 sys.path = [path]
64 try: 65 try:
65 yield 66 yield
66 finally: 67 finally:
67 sys.path = saved 68 sys.path = saved
68 69
69 70
70 def expand_root_placeholder(root, lst): 71 def expand_root_placeholder(root, lst):
71 """This expands CheckoutRootPlaceholder in paths to a real path. 72 """This expands CheckoutRootPlaceholder in paths to a real path.
72 See recipe_util.checkout_path() for usage.""" 73 See recipe_util.checkout_path() for usage."""
73 ret = [] 74 ret = []
74 replacements = {'CheckoutRootPlaceholder': root} 75 replacements = {'CheckoutRootPlaceholder': root}
75 for item in lst: 76 for item in lst:
76 if isinstance(item, str): 77 if isinstance(item, str):
77 if '%(CheckoutRootPlaceholder)s' in item: 78 if '%(CheckoutRootPlaceholder)s' in item:
78 assert root, 'Must use "checkout" key to use checkout_path().' 79 assert root, 'Must use "checkout" key to use checkout_path().'
79 ret.append(item % replacements) 80 ret.append(item % replacements)
80 continue 81 continue
81 ret.append(item) 82 ret.append(item)
82 return ret 83 return ret
83 84
84 85
85 def get_args(): 86 def get_args(argv):
86 """Process command-line arguments.""" 87 """Process command-line arguments."""
87 88
88 parser = optparse.OptionParser( 89 parser = optparse.OptionParser(
89 description='Entry point for annotated builds.') 90 description='Entry point for annotated builds.')
90 parser.add_option('--build-properties', 91 parser.add_option('--build-properties',
91 action='callback', callback=chromium_utils.convert_json, 92 action='callback', callback=chromium_utils.convert_json,
92 type='string', default={}, 93 type='string', default={},
93 help='build properties in JSON format') 94 help='build properties in JSON format')
94 parser.add_option('--factory-properties', 95 parser.add_option('--factory-properties',
95 action='callback', callback=chromium_utils.convert_json, 96 action='callback', callback=chromium_utils.convert_json,
96 type='string', default={}, 97 type='string', default={},
97 help='factory properties in JSON format') 98 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, 99 parser.add_option('--keep-stdin', action='store_true', default=False,
105 help='don\'t close stdin when running recipe steps') 100 help='don\'t close stdin when running recipe steps')
106 return parser.parse_args() 101 return parser.parse_args(argv)
107 102
108 103
109 def main(): 104 def main(argv=None):
110 opts, _ = get_args() 105 opts, _ = get_args(argv)
111 106
107 stream = annotator.StructuredAnnotationStream(seed_steps=['setup_build'])
108
109 ret = make_steps(stream, opts.build_properties, opts.factory_properties)
110
111 if isinstance(ret, int):
Vadim Shtayura 2013/05/11 00:33:32 Maybe make 'make_steps' to return a tuple (status
iannucci 2013/05/11 04:12:27 Done. (Actually made it a namedtuple)
112 return ret
113 else:
114 return run_annotator(stream, ret, opts.keep_stdin)
115
116 def make_steps(stream, build_properties, factory_properties,
117 test_mode=False):
112 # TODO(iannucci): Stop this when blamelist becomes sane data. 118 # TODO(iannucci): Stop this when blamelist becomes sane data.
113 if ('blamelist_real' in opts.build_properties and 119 if ('blamelist_real' in build_properties and
114 'blamelist' in opts.build_properties): 120 'blamelist' in build_properties):
115 opts.build_properties['blamelist'] = opts.build_properties['blamelist_real'] 121 build_properties['blamelist'] = build_properties['blamelist_real']
116 del opts.build_properties['blamelist_real'] 122 del build_properties['blamelist_real']
117 123
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: 124 with stream.step('setup_build') as s:
122 assert 'recipe' in opts.factory_properties 125 assert 'recipe' in factory_properties
123 factory_properties = opts.factory_properties 126 factory_properties = factory_properties
Vadim Shtayura 2013/05/11 00:33:32 Hmm...
iannucci 2013/05/11 04:12:27 Oops. Find/replace :D
124 recipe = factory_properties['recipe'] 127 recipe = factory_properties['recipe']
125 recipe_dirs = (os.path.abspath(p) for p in ( 128 recipe_dirs = (os.path.abspath(p) for p in (
126 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', 129 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts',
127 'slave-internal', 'recipes'), 130 'slave-internal', 'recipes'),
128 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', 131 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts',
129 'slave', 'recipes'), 132 'slave', 'recipes'),
130 os.path.join(SCRIPT_PATH, 'recipes'), 133 os.path.join(SCRIPT_PATH, 'recipes'),
131 )) 134 ))
132 135
133 for path in recipe_dirs: 136 for path in recipe_dirs:
134 recipe_module = None 137 recipe_module = None
135 with temp_purge_path(path): 138 with temp_purge_path(path):
136 try: 139 try:
137 recipe_module = __import__(recipe, globals(), locals()) 140 recipe_module = __import__(recipe, globals(), locals())
138 except ImportError: 141 except ImportError:
139 continue 142 continue
140 recipe_dict = recipe_module.GetFactoryProperties( 143 recipe_dict = recipe_module.GetFactoryProperties(
141 recipe_util, 144 recipe_util,
142 opts.factory_properties.copy(), 145 factory_properties.copy(),
143 opts.build_properties.copy()) 146 build_properties.copy())
144 break 147 break
145 else: 148 else:
146 s.step_text('recipe not found') 149 s.step_text('recipe not found')
147 s.step_failure() 150 s.step_failure()
148 return 1 151 return 1
149 152
150 factory_properties.update(recipe_dict) 153 factory_properties.update(recipe_dict)
151 154
152 # If a checkout is specified, get its type and spec and pass them 155 # If a checkout is specified, get its type and spec and pass them
153 # off to annotated_checkout.py to actually fetch the repo. 156 # off to annotated_checkout.py to actually fetch the repo.
154 # annotated_checkout.py handles its own StructuredAnnotationStream. 157 # annotated_checkout.py handles its own StructuredAnnotationStream.
155 root = None 158 root = None
156 if 'checkout' in factory_properties: 159 if 'checkout' in factory_properties:
157 checkout_type = factory_properties['checkout'] 160 checkout_type = factory_properties['checkout']
158 checkout_spec = factory_properties['%s_spec' % checkout_type] 161 checkout_spec = factory_properties['%s_spec' % checkout_type]
159 ret, root = annotated_checkout.run(checkout_type, checkout_spec) 162 ret, root = annotated_checkout.run(checkout_type, checkout_spec,
163 test_mode)
160 if ret != 0: 164 if ret != 0:
161 return ret 165 return ret
166 if test_mode:
167 root = '[BUILD_ROOT]'+root[len(BUILD_ROOT):]
162 168
163 assert ('script' in factory_properties) ^ ('steps' in factory_properties) 169 assert ('script' in factory_properties) ^ ('steps' in factory_properties)
164 ret = 0 170 ret = 0
165 171
166 # If a script is specified, import it, execute its GetSteps method, 172 # If a script is specified, import it, execute its GetSteps method,
167 # and pass those steps forward so they get executed by annotator.py. 173 # and pass those steps forward so they get executed by annotator.py.
174 # If we're in test_mode mode, just return the script.
168 if 'script' in factory_properties: 175 if 'script' in factory_properties:
169 with stream.step('get_steps') as s: 176 with stream.step('get_steps') as s:
170 assert isinstance(factory_properties['script'], str) 177 assert isinstance(factory_properties['script'], str)
171 [script] = expand_root_placeholder(root, [factory_properties['script']]) 178 [script] = expand_root_placeholder(root, [factory_properties['script']])
179 if test_mode:
180 return script
172 assert os.path.abspath(script) == script 181 assert os.path.abspath(script) == script
182
173 with temp_purge_path(os.path.dirname(script)): 183 with temp_purge_path(os.path.dirname(script)):
174 try: 184 try:
175 script_name = os.path.splitext(os.path.basename(script))[0] 185 script_name = os.path.splitext(os.path.basename(script))[0]
176 script_module = __import__(script_name, globals(), locals()) 186 script_module = __import__(script_name, globals(), locals())
177 except ImportError: 187 except ImportError:
178 s.step_text('script not found') 188 s.step_text('script not found')
179 s.step_failure() 189 s.step_failure()
180 return 1 190 return 1
181 steps_dict = script_module.GetSteps(recipe_util, 191 steps_dict = script_module.GetSteps(recipe_util,
182 opts.factory_properties.copy(), 192 factory_properties.copy(),
183 opts.build_properties.copy()) 193 build_properties.copy())
184 factory_properties['steps'] = steps_dict 194 factory_properties['steps'] = steps_dict
185 195
186 # Execute annotator.py with steps if specified. 196 # Execute annotator.py with steps if specified.
187 # annotator.py handles the seeding, execution, and annotation of each step. 197 # annotator.py handles the seeding, execution, and annotation of each step.
188 if 'steps' in factory_properties: 198 if 'steps' in factory_properties:
189 steps = factory_properties.pop('steps') 199 steps = factory_properties.pop('steps')
190 factory_properties_str = json.dumps(factory_properties) 200 factory_properties_str = json.dumps(factory_properties)
191 build_properties_str = json.dumps(opts.build_properties) 201 build_properties_str = json.dumps(build_properties)
192 property_placeholder_lst = [ 202 property_placeholder_lst = [
193 '--factory-properties', factory_properties_str, 203 '--factory-properties', factory_properties_str,
194 '--build-properties', build_properties_str] 204 '--build-properties', build_properties_str]
195 for step in steps: 205 for step in steps:
196 new_cmd = [] 206 new_cmd = []
197 for item in expand_root_placeholder(root, step['cmd']): 207 for item in expand_root_placeholder(root, step['cmd']):
198 if item == recipe_util.PropertyPlaceholder: 208 if item == recipe_util.PropertyPlaceholder:
199 new_cmd.extend(property_placeholder_lst) 209 new_cmd.extend(property_placeholder_lst)
200 else: 210 else:
201 new_cmd.append(item) 211 new_cmd.append(item)
202 step['cmd'] = new_cmd 212 step['cmd'] = new_cmd
203 if 'cwd' in step: 213 if 'cwd' in step:
204 [new_cwd] = expand_root_placeholder(root, [step['cwd']]) 214 [new_cwd] = expand_root_placeholder(root, [step['cwd']])
205 step['cwd'] = new_cwd 215 step['cwd'] = new_cwd
206 annotator_path = os.path.join( 216
207 os.path.dirname(SCRIPT_PATH), 'common', 'annotator.py') 217 return steps
208 tmpfile, tmpname = tempfile.mkstemp() 218
209 try: 219 def run_annotator(stream, steps, keep_stdin):
210 cmd = [sys.executable, annotator_path, tmpname] 220 ret = 0
211 step_doc = json.dumps(steps) 221 annotator_path = os.path.join(
212 with os.fdopen(tmpfile, 'wb') as f: 222 os.path.dirname(SCRIPT_PATH), 'common', 'annotator.py')
213 f.write(step_doc) 223 tmpfile, tmpname = tempfile.mkstemp()
214 with stream.step('annotator_preamble') as s: 224 try:
215 print 'in %s executing: %s' % (os.getcwd(), ' '.join(cmd)) 225 cmd = [sys.executable, annotator_path, tmpname]
216 print 'with: %s' % step_doc 226 step_doc = json.dumps(steps)
217 if opts.keep_stdin: 227 with os.fdopen(tmpfile, 'wb') as f:
218 ret = subprocess.call(cmd) 228 f.write(step_doc)
219 else: 229 with stream.step('annotator_preamble'):
220 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE) 230 print 'in %s executing: %s' % (os.getcwd(), ' '.join(cmd))
221 proc.communicate('') 231 print 'with: %s' % step_doc
222 ret = proc.returncode 232 if keep_stdin:
223 finally: 233 ret = subprocess.call(cmd)
224 os.unlink(tmpname) 234 else:
235 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
236 proc.communicate('')
237 ret = proc.returncode
238 finally:
239 os.unlink(tmpname)
225 240
226 return ret 241 return ret
227 242
228 243
229 def UpdateScripts(): 244 def UpdateScripts():
230 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'): 245 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'):
231 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS') 246 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS')
232 return False 247 return False
233 stream = annotator.StructuredAnnotationStream(seed_steps=['update_scripts']) 248 stream = annotator.StructuredAnnotationStream(seed_steps=['update_scripts'])
234 with stream.step('update_scripts') as s: 249 with stream.step('update_scripts') as s:
235 build_root = os.path.join(SCRIPT_PATH, '..', '..') 250 build_root = os.path.join(SCRIPT_PATH, '..', '..')
236 gclient_name = 'gclient' 251 gclient_name = 'gclient'
237 if sys.platform.startswith('win'): 252 if sys.platform.startswith('win'):
238 gclient_name += '.bat' 253 gclient_name += '.bat'
239 gclient_path = os.path.join(build_root, '..', 'depot_tools', gclient_name) 254 gclient_path = os.path.join(build_root, '..', 'depot_tools', gclient_name)
240 if subprocess.call([gclient_path, 'sync', '--force'], cwd=build_root) != 0: 255 if subprocess.call([gclient_path, 'sync', '--force'], cwd=build_root) != 0:
241 s.step_text('gclient sync failed!') 256 s.step_text('gclient sync failed!')
242 s.step_warnings() 257 s.step_warnings()
243 os.environ['RUN_SLAVE_UPDATED_SCRIPTS'] = '1' 258 os.environ['RUN_SLAVE_UPDATED_SCRIPTS'] = '1'
244 return True 259 return True
245 260
246 261
247 if __name__ == '__main__': 262 if __name__ == '__main__':
248 if UpdateScripts(): 263 if UpdateScripts():
249 os.execv(sys.executable, [sys.executable] + sys.argv) 264 os.execv(sys.executable, [sys.executable] + sys.argv)
250 sys.exit(main()) 265 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698