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

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

Issue 15270004: Add step generator protocol, remove annotated_checkout, remove script. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Address comments and change expected json output format 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
« no previous file with comments | « scripts/slave/git_setup.py ('k') | scripts/slave/recipes/gatekeeper.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """This module holds utilities which make writing recipes easier.""" 5 """This module holds utilities which make writing recipes easier."""
6 6
7 import contextlib as _contextlib 7 import contextlib as _contextlib
8 import os as _os 8 import os as _os
9 9
10 # These imports are intended to be passed through to recipes
11 # pylint: disable=W0611
12 from common.chromium_utils import IsWindows, IsMac, IsLinux
13
10 # e.g. /b/build/slave/<slave-name>/build 14 # e.g. /b/build/slave/<slave-name>/build
11 SLAVE_BUILD_ROOT = _os.path.abspath(_os.getcwd()) 15 SLAVE_BUILD_ROOT = _os.path.abspath(_os.getcwd())
12 # e.g. /b 16 # e.g. /b
13 ROOT = _os.path.abspath(_os.path.join(SLAVE_BUILD_ROOT, _os.pardir, _os.pardir, 17 ROOT = _os.path.abspath(_os.path.join(SLAVE_BUILD_ROOT, _os.pardir, _os.pardir,
14 _os.pardir, _os.pardir)) 18 _os.pardir, _os.pardir))
15 # e.g. /b/build_internal 19 # e.g. /b/build_internal
16 BUILD_INTERNAL_ROOT = _os.path.join(ROOT, 'build_internal') 20 BUILD_INTERNAL_ROOT = _os.path.join(ROOT, 'build_internal')
17 # e.g. /b/build 21 # e.g. /b/build
18 BUILD_ROOT = _os.path.join(ROOT, 'build') 22 BUILD_ROOT = _os.path.join(ROOT, 'build')
19 # e.g. /b/depot_tools 23 # e.g. /b/depot_tools
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
203 parameters --factory-properties={...} and --build-properties={...} after 207 parameters --factory-properties={...} and --build-properties={...} after
204 your recipe terminates. 208 your recipe terminates.
205 209
206 Note that the 'steps' key will be absent from factory-properties. If you 210 Note that the 'steps' key will be absent from factory-properties. If you
207 need to pass the list of steps to some of the steps, you will need to do 211 need to pass the list of steps to some of the steps, you will need to do
208 that manually in your recipe (preferably with json.dumps()). 212 that manually in your recipe (preferably with json.dumps()).
209 213
210 This placeholder can be automatically added when you use the Steps.step() 214 This placeholder can be automatically added when you use the Steps.step()
211 method in this module. 215 method in this module.
212 """ 216 """
213 pass
214 PropertyPlaceholder = PropertyPlaceholder() 217 PropertyPlaceholder = PropertyPlaceholder()
215 218
219 class JsonOutputPlaceholder(object):
220 """JsonOutputPlaceholder is meant to be a singleton object which, when added
221 to a step's cmd list, will be replaced by annotated_run with the command
222 parameters --json-output=/path/to/file during the evaluation of your recipe
223 generator.
224
225 This placeholder can be optionally added when you use the Steps.step()
226 method in this module.
227
228 After the termination of the step, this file is expected to contain a valid
229 JSON document, which will be set as the json_output for that step in the
230 step_history OrderedDict passed to your recipe generator.
231 """
232 JsonOutputPlaceholder = JsonOutputPlaceholder()
233
216 234
217 def _url_method(name): 235 def _url_method(name):
218 """Returns a shortcut static method which functions like os.path.join and uses 236 """Returns a shortcut static method which functions like os.path.join and uses
219 a fixed first url component which is chosen from the urls defined in 237 a fixed first url component which is chosen from the urls defined in
220 SOURCE_URLS based on |name|. 238 SOURCE_URLS based on |name|.
221 """ 239 """
222 # note that we do the __name__ munging for each function separately because 240 # note that we do the __name__ munging for each function separately because
223 # staticmethod hides these attributes. 241 # staticmethod hides these attributes.
224 bases = SOURCE_URLS[name] 242 bases = SOURCE_URLS[name]
225 if len(bases) == 1: 243 if len(bases) == 1:
(...skipping 24 matching lines...) Expand all
250 def __init__(self, build_properties): 268 def __init__(self, build_properties):
251 self.build_properties = build_properties 269 self.build_properties = build_properties
252 self.use_mirror = self.build_properties.get('use_mirror', True) 270 self.use_mirror = self.build_properties.get('use_mirror', True)
253 271
254 def mirror_only(self, obj): 272 def mirror_only(self, obj):
255 """Returns obj if we're using mirrors. Otherwise returns the 'empty' 273 """Returns obj if we're using mirrors. Otherwise returns the 'empty'
256 version of obj. 274 version of obj.
257 """ 275 """
258 return obj if self.use_mirror else obj.__class__() 276 return obj if self.use_mirror else obj.__class__()
259 277
260 def gclient_common_spec(self, solution_name):
261 """Returns a single gclient solution object (python dict) for common
262 solutions.
263 """
264 return GCLIENT_COMMON_SPECS[solution_name](self)
265
266 @staticmethod 278 @staticmethod
267 def step(name, cmd, add_properties=False, **kwargs): 279 def step(name, cmd, add_properties=False, add_json_output=False, **kwargs):
268 """Returns a step dictionary which is compatible with annotator.py. 280 """Returns a step dictionary which is compatible with annotator.py.
269 281
270 Uses PropertyPlaceholder as a stand-in for build-properties and 282 Uses PropertyPlaceholder as a stand-in for build-properties and
271 factory-properties so that annotated_run can fill them in after the recipe 283 factory-properties so that annotated_run can fill them in after the recipe
272 completes. 284 completes.
273 285
274 Args: 286 Args:
275 name: The name of this step. 287 name: The name of this step.
276 cmd: A list of strings in the style of subprocess.Popen. 288 cmd: A list of strings in the style of subprocess.Popen.
277 add_properties: Add PropertyPlaceholder iff True 289 add_properties: Add PropertyPlaceholder iff True
290 add_json_output: Add JsonOutputPlaceholder iff True
278 **kwargs: Additional entries to add to the annotator.py step dictionary. 291 **kwargs: Additional entries to add to the annotator.py step dictionary.
279 292
280 Returns: 293 Returns:
281 A step dictionary which is compatible with annotator.py. 294 A step dictionary which is compatible with annotator.py.
282 """ 295 """
283 assert 'shell' not in kwargs 296 assert 'shell' not in kwargs
284 assert isinstance(cmd, list) 297 assert isinstance(cmd, list)
285 if add_properties: 298 if add_properties:
286 cmd += [PropertyPlaceholder] 299 cmd += [PropertyPlaceholder]
300 if add_json_output:
301 cmd += [JsonOutputPlaceholder]
287 ret = kwargs 302 ret = kwargs
288 ret.update({'name': name, 'cmd': cmd}) 303 ret.update({'name': name, 'cmd': cmd})
289 return ret 304 return ret
290 305
291 def apply_issue_step(self, *root_pieces): 306 def apply_issue(self, *root_pieces):
292 return self.step('apply_issue', [ 307 return self.step('apply_issue', [
293 depot_tools_path('apply_issue'), 308 depot_tools_path('apply_issue'),
294 '-r', checkout_path(*root_pieces), 309 '-r', checkout_path(*root_pieces),
295 '-i', self.build_properties['issue'], 310 '-i', self.build_properties['issue'],
296 '-p', self.build_properties['patchset'], 311 '-p', self.build_properties['patchset'],
297 '-s', self.build_properties['rietveld'], 312 '-s', self.build_properties['rietveld'],
298 '-e', 'commit-bot@chromium.org']) 313 '-e', 'commit-bot@chromium.org'])
299 314
300 def git_step(self, *args): 315 def git(self, *args, **kwargs):
301 name = 'git '+args[0] 316 name = 'git '+args[0]
302 # Distinguish 'git config' commands by the variable they are setting. 317 # Distinguish 'git config' commands by the variable they are setting.
303 if args[0] == 'config' and not args[1].startswith('-'): 318 if args[0] == 'config' and not args[1].startswith('-'):
304 name += ' ' + args[1] 319 name += ' ' + args[1]
305 return self.step(name, [ 320 return self.step(name, [
306 'git', '--work-tree', checkout_path(), 321 'git', '--work-tree', checkout_path(),
307 '--git-dir', checkout_path('.git')] + list(args)) 322 '--git-dir', checkout_path('.git')] + list(args), **kwargs)
323
324 def generator_script(self, path_to_script):
325 def step_generator(step_history, _failure):
326 yield self.step(
327 'gen step(%s)' % _os.path.basename(path_to_script),
328 [path_to_script],
329 add_properties=True,
330 add_json_output=True,
331 cwd=checkout_path())
332 new_steps = step_history.last_step().json_data
333 assert isinstance(new_steps, list)
334 yield new_steps
335 return step_generator
336
337 def git_checkout(self, url, dir_path=None, branch='master', recursive=False):
338 if not dir_path:
339 dir_path = url.rsplit('/', 1)[-1]
340 if dir_path.endswith('.git'): # ex: https://host/foobar.git
341 dir_path = dir_path[:-len('.git')]
342 if not dir_path: # ex: ssh://host:repo/foobar/.git
343 dir_path = dir_path.rsplit('/', 1)[-1]
344 dir_path = slave_build_path(dir_path)
345 assert _os.pardir not in dir_path
346 recursive_args = ['--recurse-submodules'] if recursive else []
347 return [
348 self.step(
349 'git setup', [
350 build_path('scripts', 'slave', 'git_setup.py'),
351 '--path', dir_path,
352 '--url', url,
353 ],
354 static_json_data={
355 'CheckoutRoot': dir_path,
356 'CheckoutSCM': 'git',
357 'CheckoutSpec': {
358 'url': url,
359 'recursive': recursive,
360 },
361 }),
362 self.git('fetch', 'origin', *recursive_args),
363 self.git('update-ref', 'refs/heads/'+branch, 'origin/'+branch),
364 self.git('clean', '-f', '-d', '-X'),
365 self.git('checkout', '-f', branch),
366 self.git('submodule', 'update', '--init', '--recursive'),
367 ]
368
369 def gclient_checkout(self, common_repo_name, git_mode=False):
370 """Returns a step generator function for gclient checkouts."""
371 spec = GCLIENT_COMMON_SPECS[common_repo_name](self)
372 spec_string = ''
373 for key in spec:
374 # We should be using json.dumps here, but gclient directly execs the dict
375 # that it receives as the argument to --spec, so we have to have True,
376 # False, and None instead of JSON's true, false, and null.
377 spec_string += '%s = %s\n' % (key, str(spec[key]))
378 gclient = depot_tools_path('gclient') + ('.bat' if IsWindows() else '')
379
380 if not git_mode:
381 clean_step = self.step('gclient clean', [gclient, 'revert', '--nohooks'])
382 sync_step = self.step('gclient sync', [gclient, 'sync', '--nohooks'])
383 else:
384 # clean() isn't used because the gclient sync flags passed in checkout()
385 # do much the same thing, and they're more correct than doing a separate
386 # 'gclient revert' because it makes sure the other args are correct when
387 # a repo was deleted and needs to be re-cloned (notably
388 # --with_branch_heads), whereas 'revert' uses default args for clone
389 # operations.
390 #
391 # TODO(mmoss): To be like current official builders, this step could just
392 # delete the whole <slave_name>/build/ directory and start each build
393 # from scratch. That might be the least bad solution, at least until we
394 # have a reliable gclient method to produce a pristine working dir for
395 # git-based builds (e.g. maybe some combination of 'git reset/clean -fx'
396 # and removing the 'out' directory).
397 clean_step = None
398 sync_step = self.step('gclient sync', [
399 gclient, 'sync', '--verbose', '--with_branch_heads', '--nohooks',
400 '--reset', '--delete_unversioned_trees', '--force'])
401 steps = [
402 self.step(
403 'gclient setup',
404 [gclient, 'config', '--spec', spec_string],
405 static_json_data={
406 'CheckoutRoot': slave_build_path(spec['solutions'][0]['name']),
407 'CheckoutSCM': 'gclient',
408 'CheckoutSpec': spec
409 }
410 ),
411 ]
412 if clean_step:
413 steps.append(clean_step)
414 if sync_step:
415 steps.append(sync_step)
416
417 return steps
OLDNEW
« no previous file with comments | « scripts/slave/git_setup.py ('k') | scripts/slave/recipes/gatekeeper.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698