blob: 98508abae45a1c037e3441143d0ffe41e8f8b04d [file] [log] [blame]
Brian Harring3fec5a82012-03-01 05:57:03 -08001#!/usr/bin/python
2
3# Copyright (c) 2011-2012 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Main builder code for Chromium OS.
8
9Used by Chromium OS buildbot configuration for all Chromium OS builds including
10full and pre-flight-queue builds.
11"""
12
13import distutils.version
14import glob
David James58e0c092012-03-04 20:31:12 -080015import multiprocessing
Brian Harring3fec5a82012-03-01 05:57:03 -080016import optparse
17import os
18import pprint
19import sys
Ryan Cui54da0702012-04-19 18:38:08 -070020import time
Brian Harring3fec5a82012-03-01 05:57:03 -080021
22from chromite.buildbot import builderstage as bs
23from chromite.buildbot import cbuildbot_background as background
24from chromite.buildbot import cbuildbot_config
25from chromite.buildbot import cbuildbot_stages as stages
26from chromite.buildbot import cbuildbot_results as results_lib
Brian Harring3fec5a82012-03-01 05:57:03 -080027from chromite.buildbot import constants
28from chromite.buildbot import gerrit_helper
29from chromite.buildbot import patch as cros_patch
30from chromite.buildbot import remote_try
31from chromite.buildbot import repository
32from chromite.buildbot import tee
33
Brian Harringc92a7012012-02-29 10:11:34 -080034from chromite.lib import cgroups
Brian Harringa184efa2012-03-04 11:51:25 -080035from chromite.lib import cleanup
Brian Harring3fec5a82012-03-01 05:57:03 -080036from chromite.lib import cros_build_lib as cros_lib
37from chromite.lib import sudo
38
Ryan Cuiadd49122012-03-21 22:19:58 -070039
Brian Harring3fec5a82012-03-01 05:57:03 -080040cros_lib.STRICT_SUDO = True
41
42_DEFAULT_LOG_DIR = 'cbuildbot_logs'
43_BUILDBOT_LOG_FILE = 'cbuildbot.log'
44_DEFAULT_EXT_BUILDROOT = 'trybot'
45_DEFAULT_INT_BUILDROOT = 'trybot-internal'
46_PATH_TO_CBUILDBOT = 'chromite/bin/cbuildbot'
47_DISTRIBUTED_TYPES = [constants.COMMIT_QUEUE_TYPE, constants.PFQ_TYPE,
48 constants.CANARY_TYPE, constants.CHROME_PFQ_TYPE,
49 constants.PALADIN_TYPE]
Brian Harring351ce442012-03-09 16:38:14 -080050_BUILDBOT_REQUIRED_BINARIES = ('pbzip2',)
Brian Harring3fec5a82012-03-01 05:57:03 -080051
52
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070053def _PrintValidConfigs(display_all=False):
Brian Harring3fec5a82012-03-01 05:57:03 -080054 """Print a list of valid buildbot configs.
55
56 Arguments:
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070057 display_all: Print all configs. Otherwise, prints only configs with
58 trybot_list=True.
Brian Harring3fec5a82012-03-01 05:57:03 -080059 """
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070060 def _GetSortKey(config_name):
61 config_dict = cbuildbot_config.config[config_name]
62 return (not config_dict['trybot_list'], config_dict['description'],
63 config_name)
64
Brian Harring3fec5a82012-03-01 05:57:03 -080065 COLUMN_WIDTH = 45
66 print 'config'.ljust(COLUMN_WIDTH), 'description'
67 print '------'.ljust(COLUMN_WIDTH), '-----------'
68 config_names = cbuildbot_config.config.keys()
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070069 config_names.sort(key=_GetSortKey)
Brian Harring3fec5a82012-03-01 05:57:03 -080070 for name in config_names:
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070071 if display_all or cbuildbot_config.config[name]['trybot_list']:
72 desc = cbuildbot_config.config[name].get('description')
73 desc = desc if desc else ''
Brian Harring3fec5a82012-03-01 05:57:03 -080074 print name.ljust(COLUMN_WIDTH), desc
75
76
77def _GetConfig(config_name):
78 """Gets the configuration for the build"""
79 if not cbuildbot_config.config.has_key(config_name):
80 print 'Non-existent configuration %s specified.' % config_name
81 print 'Please specify one of:'
82 _PrintValidConfigs()
83 sys.exit(1)
84
85 result = cbuildbot_config.config[config_name]
86
87 return result
88
89
90def _GetChromiteTrackingBranch():
David James66009462012-03-25 10:08:38 -070091 """Returns the remote branch associated with chromite."""
Brian Harring3fec5a82012-03-01 05:57:03 -080092 cwd = os.path.dirname(os.path.realpath(__file__))
David James66009462012-03-25 10:08:38 -070093 branch = cros_lib.GetCurrentBranch(cwd)
94 if branch:
95 tracking_branch = cros_lib.GetTrackingBranch(branch, cwd)[1]
96 if tracking_branch.startswith('refs/heads/'):
97 return tracking_branch.replace('refs/heads/', '')
98 # If we are not on a branch, or if the tracking branch is a revision,
David James8b3c1bf2012-03-28 09:10:16 -070099 # use the push branch. For repo repositories, this will be the manifest
100 # branch configured for this project. For other repositories, we'll just
101 # guess 'master', since there's no easy way to find out what branch
102 # we're on.
103 return cros_lib.GetPushBranch(cwd)[1]
Brian Harring3fec5a82012-03-01 05:57:03 -0800104
105
106def _CheckBuildRootBranch(buildroot, tracking_branch):
107 """Make sure buildroot branch is the same as Chromite branch."""
108 manifest_branch = cros_lib.GetManifestDefaultBranch(buildroot)
109 if manifest_branch != tracking_branch:
110 cros_lib.Die('Chromite is not on same branch as buildroot checkout\n' +
111 'Chromite is on branch %s.\n' % tracking_branch +
112 'Buildroot checked out to %s\n' % manifest_branch)
113
114
115def _PreProcessPatches(gerrit_patches, local_patches):
116 """Validate patches ASAP to catch user errors. Also generate patch info.
117
118 Args:
119 gerrit_patches: List of gerrit CL ID's passed in by user.
120 local_patches: List of local project branches to generate patches from.
121
122 Returns:
123 A tuple containing a list of cros_patch.GerritPatch and a list of
Matt Tennantd55b1f42012-04-13 14:15:01 -0700124 cros_patch.LocalGitRepoPatch objects.
Brian Harring3fec5a82012-03-01 05:57:03 -0800125 """
126 gerrit_patch_info = []
127 local_patch_info = []
128
129 try:
130 if gerrit_patches:
131 gerrit_patch_info = gerrit_helper.GetGerritPatchInfo(gerrit_patches)
132 for patch in gerrit_patch_info:
133 if patch.IsAlreadyMerged():
134 cros_lib.Warning('Patch %s has already been merged.' % str(patch))
135 except gerrit_helper.GerritException as e:
136 cros_lib.Die(str(e))
137
138 try:
139 if local_patches:
140 local_patch_info = cros_patch.PrepareLocalPatches(
141 local_patches,
142 _GetChromiteTrackingBranch())
143
144 except cros_patch.PatchException as e:
145 cros_lib.Die(str(e))
146
147 return gerrit_patch_info, local_patch_info
148
149
150def _IsIncrementalBuild(buildroot, clobber):
151 """Returns True if we are reusing an existing buildroot."""
152 repo_dir = os.path.join(buildroot, '.repo')
153 return not clobber and os.path.isdir(repo_dir)
154
155
156class Builder(object):
157 """Parent class for all builder types.
158
159 This class functions as a parent class for various build types. It's intended
160 use is builder_instance.Run().
161
162 Vars:
Brian Harring3fec5a82012-03-01 05:57:03 -0800163 build_config: The configuration dictionary from cbuildbot_config.
164 options: The options provided from optparse in main().
165 completed_stages_file: Where we store resume state.
166 archive_url: Where our artifacts for this builder will be archived.
167 tracking_branch: The tracking branch for this build.
168 release_tag: The associated "chrome os version" of this build.
169 gerrit_patches: Gerrit patches to be included in build.
170 local_patches: Local patches to be included in build.
171 """
172
David James944a48e2012-03-07 12:19:03 -0800173 def __init__(self, options, build_config):
Brian Harring3fec5a82012-03-01 05:57:03 -0800174 """Initializes instance variables. Must be called by all subclasses."""
Brian Harring3fec5a82012-03-01 05:57:03 -0800175 self.build_config = build_config
176 self.options = options
177
178 # TODO, Remove here and in config after bug chromium-os:14649 is fixed.
179 if self.build_config['chromeos_official']:
180 os.environ['CHROMEOS_OFFICIAL'] = '1'
181
182 self.completed_stages_file = os.path.join(options.buildroot,
183 '.completed_stages')
David James58e0c092012-03-04 20:31:12 -0800184 self.archive_stages = {}
Brian Harring3fec5a82012-03-01 05:57:03 -0800185 self.archive_urls = {}
186 self.release_tag = None
187 self.tracking_branch = _GetChromiteTrackingBranch()
188 self.gerrit_patches = None
189 self.local_patches = None
190
191 def Initialize(self):
192 """Runs through the initialization steps of an actual build."""
193 if self.options.resume and os.path.exists(self.completed_stages_file):
194 with open(self.completed_stages_file, 'r') as load_file:
195 results_lib.Results.RestoreCompletedStages(load_file)
196
197 # We only want to do this if we need to patch changes.
198 if not results_lib.Results.GetPrevious().get(
199 self._GetStageInstance(stages.PatchChangesStage, None, None).name):
200 self.gerrit_patches, self.local_patches = _PreProcessPatches(
201 self.options.gerrit_patches, self.options.local_patches)
202
203 bs.BuilderStage.SetTrackingBranch(self.tracking_branch)
204
205 # Check branch matching early.
206 if _IsIncrementalBuild(self.options.buildroot, self.options.clobber):
207 _CheckBuildRootBranch(self.options.buildroot, self.tracking_branch)
208
209 self._RunStage(stages.CleanUpStage)
210
211 def _GetStageInstance(self, stage, *args, **kwargs):
212 """Helper function to get an instance given the args.
213
David James944a48e2012-03-07 12:19:03 -0800214 Useful as almost all stages just take in options and build_config.
Brian Harring3fec5a82012-03-01 05:57:03 -0800215 """
David James944a48e2012-03-07 12:19:03 -0800216 config = kwargs.pop('config', self.build_config)
217 return stage(self.options, config, *args, **kwargs)
Brian Harring3fec5a82012-03-01 05:57:03 -0800218
219 def _SetReleaseTag(self):
220 """Sets the release tag from the manifest_manager.
221
222 Must be run after sync stage as syncing enables us to have a release tag.
223 """
224 # Extract version we have decided to build into self.release_tag.
225 manifest_manager = stages.ManifestVersionedSyncStage.manifest_manager
226 if manifest_manager:
227 self.release_tag = manifest_manager.current_version
228
229 def _RunStage(self, stage, *args, **kwargs):
230 """Wrapper to run a stage."""
231 stage_instance = self._GetStageInstance(stage, *args, **kwargs)
232 return stage_instance.Run()
233
234 def GetSyncInstance(self):
235 """Returns an instance of a SyncStage that should be run.
236
237 Subclasses must override this method.
238 """
239 raise NotImplementedError()
240
241 def RunStages(self):
242 """Subclasses must override this method. Runs the appropriate code."""
243 raise NotImplementedError()
244
245 def _WriteCheckpoint(self):
246 """Drops a completed stages file with current state."""
247 with open(self.completed_stages_file, 'w+') as save_file:
248 results_lib.Results.SaveCompletedStages(save_file)
249
250 def _ShouldReExecuteInBuildRoot(self):
251 """Returns True if this build should be re-executed in the buildroot."""
252 abs_buildroot = os.path.abspath(self.options.buildroot)
253 return not os.path.abspath(__file__).startswith(abs_buildroot)
254
255 def _ReExecuteInBuildroot(self, sync_instance):
256 """Reexecutes self in buildroot and returns True if build succeeds.
257
258 This allows the buildbot code to test itself when changes are patched for
259 buildbot-related code. This is a no-op if the buildroot == buildroot
260 of the running chromite checkout.
261
262 Args:
263 sync_instance: Instance of the sync stage that was run to sync.
264
265 Returns:
266 True if the Build succeeded.
267 """
268 # If we are resuming, use last checkpoint.
269 if not self.options.resume:
270 self._WriteCheckpoint()
271
272 # Re-write paths to use absolute paths.
273 # Suppress any timeout options given from the commandline in the
274 # invoked cbuildbot; our timeout will enforce it instead.
275 args_to_append = ['--resume', '--timeout', '0', '--buildroot',
276 os.path.abspath(self.options.buildroot)]
277
278 if self.options.chrome_root:
279 args_to_append += ['--chrome_root',
280 os.path.abspath(self.options.chrome_root)]
281
282 if stages.ManifestVersionedSyncStage.manifest_manager:
283 ver = stages.ManifestVersionedSyncStage.manifest_manager.current_version
284 args_to_append += ['--version', ver]
285
286 if isinstance(sync_instance, stages.CommitQueueSyncStage):
287 vp_file = sync_instance.SaveValidationPool()
288 args_to_append += ['--validation_pool', vp_file]
289
290 # Re-run the command in the buildroot.
291 # Finally, be generous and give the invoked cbuildbot 30s to shutdown
292 # when something occurs. It should exit quicker, but the sigterm may
293 # hit while the system is particularly busy.
294 return_obj = cros_lib.RunCommand(
295 [_PATH_TO_CBUILDBOT] + sys.argv[1:] + args_to_append,
296 cwd=self.options.buildroot, error_code_ok=True, kill_timeout=30)
297 return return_obj.returncode == 0
298
299 def Run(self):
300 """Main runner for this builder class. Runs build and prints summary."""
301 print_report = True
302 success = True
303 try:
304 self.Initialize()
305 sync_instance = self.GetSyncInstance()
306 sync_instance.Run()
307 self._SetReleaseTag()
308
Ryan Cuicedd8a52012-03-22 02:28:35 -0700309 if (self.gerrit_patches or self.local_patches
310 or self.options.remote_patches):
Brian Harring3fec5a82012-03-01 05:57:03 -0800311 self._RunStage(stages.PatchChangesStage,
312 self.gerrit_patches, self.local_patches)
313
314 if self._ShouldReExecuteInBuildRoot():
315 print_report = False
316 success = self._ReExecuteInBuildroot(sync_instance)
317 else:
318 self.RunStages()
319
320 finally:
321 if print_report:
322 self._WriteCheckpoint()
323 print '\n\n\n@@@BUILD_STEP Report@@@\n'
324 results_lib.Results.Report(sys.stdout, self.archive_urls,
325 self.release_tag)
326 success = results_lib.Results.BuildSucceededSoFar()
327
328 return success
329
330
331class SimpleBuilder(Builder):
332 """Builder that performs basic vetting operations."""
333
334 def GetSyncInstance(self):
335 """Sync to lkgm or TOT as necessary.
336
337 Returns: the instance of the sync stage that was run.
338 """
339 if self.options.lkgm or self.build_config['use_lkgm']:
340 sync_stage = self._GetStageInstance(stages.LKGMSyncStage)
341 else:
342 sync_stage = self._GetStageInstance(stages.SyncStage)
343
344 return sync_stage
345
David James58e0c092012-03-04 20:31:12 -0800346 def _RunBackgroundStagesForBoard(self, board):
347 """Run background board-specific stages for the specified board."""
David James58e0c092012-03-04 20:31:12 -0800348 archive_stage = self.archive_stages[board]
David James944a48e2012-03-07 12:19:03 -0800349 configs = self.build_config['board_specific_configs']
350 config = configs.get(board, self.build_config)
351 stage_list = [[stages.VMTestStage, board, archive_stage],
352 [stages.ChromeTestStage, board, archive_stage],
353 [stages.UnitTestStage, board],
354 [stages.UploadPrebuiltsStage, board]]
Brian Harring3fec5a82012-03-01 05:57:03 -0800355
David James58e0c092012-03-04 20:31:12 -0800356 # We can not run hw tests without archiving the payloads.
357 if self.options.archive:
David James944a48e2012-03-07 12:19:03 -0800358 for suite in config['hw_tests']:
359 stage_list.append([stages.HWTestStage, board, archive_stage, suite])
Chris Sosab50dc932012-03-01 14:00:58 -0800360
David James944a48e2012-03-07 12:19:03 -0800361 steps = [self._GetStageInstance(*x, config=config).Run for x in stage_list]
362 background.RunParallelSteps(steps + [archive_stage.Run])
Brian Harring3fec5a82012-03-01 05:57:03 -0800363
364 def RunStages(self):
365 """Runs through build process."""
366 self._RunStage(stages.BuildBoardStage)
367
368 # TODO(sosa): Split these out into classes.
Brian Harring3fec5a82012-03-01 05:57:03 -0800369 if self.build_config['build_type'] == constants.CHROOT_BUILDER_TYPE:
370 self._RunStage(stages.SDKTestStage)
371 self._RunStage(stages.UploadPrebuiltsStage,
372 constants.CHROOT_BUILDER_BOARD)
373 elif self.build_config['build_type'] == constants.REFRESH_PACKAGES_TYPE:
374 self._RunStage(stages.RefreshPackageStatusStage)
375 else:
376 self._RunStage(stages.UprevStage)
Brian Harring3fec5a82012-03-01 05:57:03 -0800377
David James944a48e2012-03-07 12:19:03 -0800378 configs = self.build_config['board_specific_configs']
David James58e0c092012-03-04 20:31:12 -0800379 for board in self.build_config['boards']:
David James944a48e2012-03-07 12:19:03 -0800380 config = configs.get(board, self.build_config)
381 archive_stage = self._GetStageInstance(stages.ArchiveStage, board,
382 config=config)
David James58e0c092012-03-04 20:31:12 -0800383 self.archive_stages[board] = archive_stage
384
David James944a48e2012-03-07 12:19:03 -0800385 # Set up a process pool to run test/archive stages in the background.
386 # This process runs task(board) for each board added to the queue.
David James58e0c092012-03-04 20:31:12 -0800387 queue = multiprocessing.Queue()
388 task = self._RunBackgroundStagesForBoard
389 with background.BackgroundTaskRunner(queue, task):
David James944a48e2012-03-07 12:19:03 -0800390 for board in self.build_config['boards']:
David James58e0c092012-03-04 20:31:12 -0800391 # Run BuildTarget in the foreground.
David James944a48e2012-03-07 12:19:03 -0800392 archive_stage = self.archive_stages[board]
393 config = configs.get(board, self.build_config)
394 self._RunStage(stages.BuildTargetStage, board, archive_stage,
Chris Sosa1a87b3e2012-04-12 13:20:42 -0700395 self.release_tag, config=config)
David James58e0c092012-03-04 20:31:12 -0800396 self.archive_urls[board] = archive_stage.GetDownloadUrl()
397
David James944a48e2012-03-07 12:19:03 -0800398 # Kick off task(board) in the background.
David James58e0c092012-03-04 20:31:12 -0800399 queue.put([board])
400
Brian Harring3fec5a82012-03-01 05:57:03 -0800401
402class DistributedBuilder(SimpleBuilder):
403 """Build class that has special logic to handle distributed builds.
404
405 These builds sync using git/manifest logic in manifest_versions. In general
406 they use a non-distributed builder code for the bulk of the work.
407 """
David James944a48e2012-03-07 12:19:03 -0800408 def __init__(self, options, build_config):
Brian Harring3fec5a82012-03-01 05:57:03 -0800409 """Initializes a buildbot builder.
410
411 Extra variables:
412 completion_stage_class: Stage used to complete a build. Set in the Sync
413 stage.
414 """
David James944a48e2012-03-07 12:19:03 -0800415 super(DistributedBuilder, self).__init__(options, build_config)
Brian Harring3fec5a82012-03-01 05:57:03 -0800416 self.completion_stage_class = None
417
418 def GetSyncInstance(self):
419 """Syncs the tree using one of the distributed sync logic paths.
420
421 Returns: the instance of the sync stage that was run.
422 """
423 # Determine sync class to use. CQ overrides PFQ bits so should check it
424 # first.
425 if cbuildbot_config.IsCQType(self.build_config['build_type']):
426 sync_stage = self._GetStageInstance(stages.CommitQueueSyncStage)
427 self.completion_stage_class = stages.CommitQueueCompletionStage
428 elif cbuildbot_config.IsPFQType(self.build_config['build_type']):
429 sync_stage = self._GetStageInstance(stages.LKGMCandidateSyncStage)
430 self.completion_stage_class = stages.LKGMCandidateSyncCompletionStage
431 else:
432 sync_stage = self._GetStageInstance(stages.ManifestVersionedSyncStage)
433 self.completion_stage_class = stages.ManifestVersionedSyncCompletionStage
434
435 return sync_stage
436
437 def Publish(self, was_build_successful):
438 """Completes build by publishing any required information."""
439 completion_stage = self._GetStageInstance(self.completion_stage_class,
440 was_build_successful)
441 completion_stage.Run()
442 name = completion_stage.name
443 if not results_lib.Results.WasStageSuccessful(name):
444 should_publish_changes = False
445 else:
446 should_publish_changes = (self.build_config['master'] and
447 was_build_successful)
448
449 if should_publish_changes:
450 self._RunStage(stages.PublishUprevChangesStage)
451
452 def RunStages(self):
453 """Runs simple builder logic and publishes information to overlays."""
454 was_build_successful = False
455 try:
David Jamesf55709e2012-03-13 09:10:15 -0700456 super(DistributedBuilder, self).RunStages()
457 was_build_successful = results_lib.Results.BuildSucceededSoFar()
Brian Harring3fec5a82012-03-01 05:57:03 -0800458 except SystemExit as ex:
459 # If a stage calls sys.exit(0), it's exiting with success, so that means
460 # we should mark ourselves as successful.
461 if ex.code == 0:
462 was_build_successful = True
463 raise
464 finally:
465 self.Publish(was_build_successful)
466
Brian Harring3fec5a82012-03-01 05:57:03 -0800467
468def _ConfirmBuildRoot(buildroot):
469 """Confirm with user the inferred buildroot, and mark it as confirmed."""
470 warning = 'Using default directory %s as buildroot' % buildroot
471 response = cros_lib.YesNoPrompt(default=cros_lib.NO, warning=warning,
472 full=True)
473 if response == cros_lib.NO:
474 print('Please specify a buildroot with the --buildroot option.')
475 sys.exit(0)
476
477 if not os.path.exists(buildroot):
478 os.mkdir(buildroot)
479
480 repository.CreateTrybotMarker(buildroot)
481
482
483def _DetermineDefaultBuildRoot(internal_build):
484 """Default buildroot to be under the directory that contains current checkout.
485
486 Arguments:
487 internal_build: Whether the build is an internal build
488 """
489 repo_dir = cros_lib.FindRepoDir()
490 if not repo_dir:
491 cros_lib.Die('Could not find root of local checkout. Please specify'
492 'using --buildroot option.')
493
494 # Place trybot buildroot under the directory containing current checkout.
495 top_level = os.path.dirname(os.path.realpath(os.path.dirname(repo_dir)))
496 if internal_build:
497 buildroot = os.path.join(top_level, _DEFAULT_INT_BUILDROOT)
498 else:
499 buildroot = os.path.join(top_level, _DEFAULT_EXT_BUILDROOT)
500
501 return buildroot
502
503
504def _BackupPreviousLog(log_file, backup_limit=25):
505 """Rename previous log.
506
507 Args:
508 log_file: The absolute path to the previous log.
509 """
510 if os.path.exists(log_file):
511 old_logs = sorted(glob.glob(log_file + '.*'),
512 key=distutils.version.LooseVersion)
513
514 if len(old_logs) >= backup_limit:
515 os.remove(old_logs[0])
516
517 last = 0
518 if old_logs:
519 last = int(old_logs.pop().rpartition('.')[2])
520
521 os.rename(log_file, log_file + '.' + str(last + 1))
522
523
David James944a48e2012-03-07 12:19:03 -0800524def _RunBuildStagesWrapper(options, build_config):
Brian Harring3fec5a82012-03-01 05:57:03 -0800525 """Helper function that wraps RunBuildStages()."""
526 def IsDistributedBuilder():
527 """Determines whether the build_config should be a DistributedBuilder."""
528 if not options.buildbot:
529 return False
530 elif build_config['build_type'] in _DISTRIBUTED_TYPES:
531 chrome_rev = build_config['chrome_rev']
532 if options.chrome_rev: chrome_rev = options.chrome_rev
533 # We don't do distributed logic to TOT Chrome PFQ's, nor local
534 # chrome roots (e.g. chrome try bots)
535 if chrome_rev not in [constants.CHROME_REV_TOT,
536 constants.CHROME_REV_LOCAL,
537 constants.CHROME_REV_SPEC]:
538 return True
539
540 return False
541
542 # Start tee-ing output to file.
543 log_file = None
544 if options.tee:
545 default_dir = os.path.join(options.buildroot, _DEFAULT_LOG_DIR)
546 dirname = options.log_dir or default_dir
547 log_file = os.path.join(dirname, _BUILDBOT_LOG_FILE)
548
549 cros_lib.SafeMakedirs(dirname)
550 _BackupPreviousLog(log_file)
551
552 try:
553 with cros_lib.AllowDisabling(options.tee, tee.Tee, log_file):
554 cros_lib.Info("cbuildbot executed with args %s"
555 % ' '.join(map(repr, sys.argv)))
556 if IsDistributedBuilder():
David James944a48e2012-03-07 12:19:03 -0800557 buildbot = DistributedBuilder(options, build_config)
Brian Harring3fec5a82012-03-01 05:57:03 -0800558 else:
David James944a48e2012-03-07 12:19:03 -0800559 buildbot = SimpleBuilder(options, build_config)
Brian Harring3fec5a82012-03-01 05:57:03 -0800560
561 if not buildbot.Run():
562 sys.exit(1)
563 finally:
564 if options.tee:
565 cros_lib.Info('Output should be saved to %s' % log_file)
566
567
568# Parser related functions
Ryan Cuicedd8a52012-03-22 02:28:35 -0700569def _CheckLocalPatches(local_patches):
Brian Harring3fec5a82012-03-01 05:57:03 -0800570 """Do an early quick check of the passed-in patches.
571
572 If the branch of a project is not specified we append the current branch the
573 project is on.
574 """
Ryan Cuicedd8a52012-03-22 02:28:35 -0700575 verified_patches = []
576 for patch in local_patches:
Brian Harring3fec5a82012-03-01 05:57:03 -0800577 components = patch.split(':')
578 if len(components) > 2:
579 msg = 'Specify local patches in project[:branch] format.'
580 raise optparse.OptionValueError(msg)
581
582 # validate project
583 project = components[0]
584 if not cros_lib.DoesProjectExist('.', project):
585 raise optparse.OptionValueError('Project %s does not exist.' % project)
586
587 project_dir = cros_lib.GetProjectDir('.', project)
588
589 # If no branch was specified, we use the project's current branch.
590 if len(components) == 1:
591 branch = cros_lib.GetCurrentBranch(project_dir)
592 if not branch:
593 raise optparse.OptionValueError('project %s is not on a branch!'
594 % project)
595 # Append branch information to patch
596 patch = '%s:%s' % (project, branch)
597 else:
598 branch = components[1]
599 if not cros_lib.DoesLocalBranchExist(project_dir, branch):
600 raise optparse.OptionValueError('Project %s does not have branch %s'
601 % (project, branch))
602
Ryan Cuicedd8a52012-03-22 02:28:35 -0700603 verified_patches.append(patch)
Brian Harring3fec5a82012-03-01 05:57:03 -0800604
Ryan Cuicedd8a52012-03-22 02:28:35 -0700605 return verified_patches
Brian Harring3fec5a82012-03-01 05:57:03 -0800606
607
608def _CheckBuildRootOption(_option, _opt_str, value, parser):
609 """Validate and convert buildroot to full-path form."""
610 value = value.strip()
611 if not value or value == '/':
612 raise optparse.OptionValueError('Invalid buildroot specified')
613
614 parser.values.buildroot = os.path.realpath(os.path.expanduser(value))
615
616
617def _CheckLogDirOption(_option, _opt_str, value, parser):
618 """Validate and convert buildroot to full-path form."""
619 parser.values.log_dir = os.path.abspath(os.path.expanduser(value))
620
621
622def _CheckChromeVersionOption(_option, _opt_str, value, parser):
623 """Upgrade other options based on chrome_version being passed."""
624 value = value.strip()
625
626 if parser.values.chrome_rev is None and value:
627 parser.values.chrome_rev = constants.CHROME_REV_SPEC
628
629 parser.values.chrome_version = value
630
631
632def _CheckChromeRootOption(_option, _opt_str, value, parser):
633 """Validate and convert chrome_root to full-path form."""
634 value = value.strip()
635 if not value or value == '/':
636 raise optparse.OptionValueError('Invalid chrome_root specified')
637
638 if parser.values.chrome_rev is None:
639 parser.values.chrome_rev = constants.CHROME_REV_LOCAL
640
641 parser.values.chrome_root = os.path.realpath(os.path.expanduser(value))
642
643
644def _CheckChromeRevOption(_option, _opt_str, value, parser):
645 """Validate the chrome_rev option."""
646 value = value.strip()
647 if value not in constants.VALID_CHROME_REVISIONS:
648 raise optparse.OptionValueError('Invalid chrome rev specified')
649
650 parser.values.chrome_rev = value
651
652
653def _CreateParser():
654 """Generate and return the parser with all the options."""
655 # Parse options
656 usage = "usage: %prog [options] buildbot_config"
657 parser = optparse.OptionParser(usage=usage)
658
659 # Main options
660 parser.add_option('-a', '--all', action='store_true', dest='print_all',
661 default=False,
662 help=('List all of the buildbot configs available. Use '
663 'with the --list option'))
664 parser.add_option('-r', '--buildroot', action='callback', dest='buildroot',
665 type='string', callback=_CheckBuildRootOption,
666 help=('Root directory where source is checked out to, and '
667 'where the build occurs. For external build configs, '
668 "defaults to 'trybot' directory at top level of your "
669 'repo-managed checkout.'))
670 parser.add_option('--chrome_rev', default=None, type='string',
671 action='callback', dest='chrome_rev',
672 callback=_CheckChromeRevOption,
673 help=('Revision of Chrome to use, of type '
674 '[%s]' % '|'.join(constants.VALID_CHROME_REVISIONS)))
Ryan Cuicedd8a52012-03-22 02:28:35 -0700675 parser.add_option('-g', '--gerrit-patches', action='append', default=[],
676 type='string', metavar="'Id1 *int_Id2...IdN'",
Brian Harring3fec5a82012-03-01 05:57:03 -0800677 help=("Space-separated list of short-form Gerrit "
678 "Change-Id's or change numbers to patch. Please "
679 "prepend '*' to internal Change-Id's"))
680 parser.add_option('-l', '--list', action='store_true', dest='list',
681 default=False,
682 help=('List the suggested trybot configs to use. Use '
683 '--all to list all of the available configs.'))
Ryan Cui54da0702012-04-19 18:38:08 -0700684 parser.add_option('--local', default=False, action='store_true',
685 help=('Specifies that this tryjob should be run locally.'))
Ryan Cuicedd8a52012-03-22 02:28:35 -0700686 parser.add_option('-p', '--local-patches', action='append', default=[],
Brian Harring3fec5a82012-03-01 05:57:03 -0800687 metavar="'<project1>[:<branch1>]...<projectN>[:<branchN>]'",
688 help=('Space-separated list of project branches with '
689 'patches to apply. Projects are specified by name. '
690 'If no branch is specified the current branch of the '
691 'project will be used.'))
692 parser.add_option('--profile', default=None, type='string', action='store',
693 dest='profile',
694 help=('Name of profile to sub-specify board variant.'))
695 parser.add_option('--remote', default=False, action='store_true',
Brian Harring3fec5a82012-03-01 05:57:03 -0800696 help=('Specifies that this tryjob should be run remotely.'))
697
698 # Advanced options
699 group = optparse.OptionGroup(
700 parser,
701 'Advanced Options',
702 'Caution: use these options at your own risk.')
703
704 group.add_option('--buildbot', dest='buildbot', action='store_true',
705 default=False, help='This is running on a buildbot')
706 group.add_option('--buildnumber',
707 help='build number', type='int', default=0)
708 group.add_option('--chrome_root', default=None, type='string',
709 action='callback', dest='chrome_root',
710 callback=_CheckChromeRootOption,
711 help='Local checkout of Chrome to use.')
712 group.add_option('--chrome_version', default=None, type='string',
713 action='callback', dest='chrome_version',
714 callback=_CheckChromeVersionOption,
715 help='Used with SPEC logic to force a particular SVN '
716 'revision of chrome rather than the latest.')
717 group.add_option('--clobber', action='store_true', dest='clobber',
718 default=False,
719 help='Clears an old checkout before syncing')
720 group.add_option('--lkgm', action='store_true', dest='lkgm', default=False,
721 help='Sync to last known good manifest blessed by PFQ')
722 parser.add_option('--log_dir', action='callback', dest='log_dir',
723 type='string', callback=_CheckLogDirOption,
724 help=('Directory where logs are stored.'))
725 group.add_option('--maxarchives', dest='max_archive_builds',
726 default=3, type='int',
727 help="Change the local saved build count limit.")
728 group.add_option('--noarchive', action='store_false', dest='archive',
729 default=True,
730 help="Don't run archive stage.")
731 group.add_option('--nobuild', action='store_false', dest='build',
732 default=True,
Matt Tennantd55b1f42012-04-13 14:15:01 -0700733 help="Don't actually build (for cbuildbot dev)")
Brian Harring3fec5a82012-03-01 05:57:03 -0800734 group.add_option('--noclean', action='store_false', dest='clean',
735 default=True,
736 help="Don't clean the buildroot")
737 group.add_option('--noprebuilts', action='store_false', dest='prebuilts',
738 default=True,
739 help="Don't upload prebuilts.")
740 group.add_option('--nosync', action='store_false', dest='sync',
741 default=True,
742 help="Don't sync before building.")
743 group.add_option('--nocgroups', action='store_false', dest='cgroups',
744 default=True,
745 help='Disable cbuildbots usage of cgroups.')
Ryan Cuiadd49122012-03-21 22:19:58 -0700746 group.add_option('--notests', action='store_false', dest='tests',
747 default=True,
748 help='Override values from buildconfig and run no tests.')
749 group.add_option('--nouprev', action='store_false', dest='uprev',
750 default=True,
751 help='Override values from buildconfig and never uprev.')
Brian Harring3fec5a82012-03-01 05:57:03 -0800752 group.add_option('--reference-repo', action='store', default=None,
753 dest='reference_repo',
754 help='Reuse git data stored in an existing repo '
755 'checkout. This can drastically reduce the network '
756 'time spent setting up the trybot checkout. By '
757 "default, if this option isn't given but cbuildbot "
758 'is invoked from a repo checkout, cbuildbot will '
759 'use the repo root.')
Ryan Cuicedd8a52012-03-22 02:28:35 -0700760 # Indicates this is running on a remote trybot machine. '
Ryan Cuiba41ad32012-03-08 17:15:29 -0800761 group.add_option('--remote-trybot', dest='remote_trybot', action='store_true',
Ryan Cuicedd8a52012-03-22 02:28:35 -0700762 default=False, help=optparse.SUPPRESS_HELP)
763 # Patches uploaded by trybot client when run using the -p option.
764 group.add_option('--remote-patches', action='append', default=[],
765 help=optparse.SUPPRESS_HELP)
766 group.add_option('--resume', action='store_true', default=False,
Ryan Cuiadd49122012-03-21 22:19:58 -0700767 help='Skip stages already successfully completed.')
Brian Harring3fec5a82012-03-01 05:57:03 -0800768 group.add_option('--timeout', action='store', type='int', default=0,
769 help="Specify the maximum amount of time this job can run "
770 "for, at which point the build will be aborted. If "
771 "set to zero, then there is no timeout")
Ryan Cui39bdbbf2012-02-29 16:15:39 -0800772 group.add_option('--test-tryjob', action='store_true',
773 default=False,
774 help='Submit a tryjob to the test repository. Will not '
775 'show up on the production trybot waterfall.')
Brian Harring3fec5a82012-03-01 05:57:03 -0800776 group.add_option('--validation_pool', default=None,
777 help='Path to a pickled validation pool. Intended for use '
778 'only with the commit queue.')
779 group.add_option('--version', dest='force_version', default=None,
780 help='Used with manifest logic. Forces use of this version '
781 'rather than create or get latest.')
782
783 parser.add_option_group(group)
784
785 # Debug options
786 group = optparse.OptionGroup(parser, "Debug Options")
787
Ryan Cui85867972012-02-23 18:21:49 -0800788 group.add_option('--debug', action='store_true', default=None,
Brian Harring3fec5a82012-03-01 05:57:03 -0800789 help='Override some options to run as a developer.')
790 group.add_option('--dump_config', action='store_true', dest='dump_config',
791 default=False,
792 help='Dump out build config options, and exit.')
793 group.add_option('--notee', action='store_false', dest='tee', default=True,
794 help="Disable logging and internal tee process. Primarily "
795 "used for debugging cbuildbot itself.")
796 parser.add_option_group(group)
797 return parser
798
799
Ryan Cui85867972012-02-23 18:21:49 -0800800def _FinishParsing(options, args):
801 """Perform some parsing tasks that need to take place after optparse.
802
803 This function needs to be easily testable! Keep it free of
804 environment-dependent code. Put more detailed usage validation in
805 _PostParseCheck().
Brian Harring3fec5a82012-03-01 05:57:03 -0800806
807 Args:
Ryan Cui85867972012-02-23 18:21:49 -0800808 options, args: The options/args object returned by optparse
Brian Harring3fec5a82012-03-01 05:57:03 -0800809 """
Brian Harring3fec5a82012-03-01 05:57:03 -0800810 if options.chrome_root:
811 if options.chrome_rev != constants.CHROME_REV_LOCAL:
812 cros_lib.Die('Chrome rev must be %s if chrome_root is set.' %
813 constants.CHROME_REV_LOCAL)
814 else:
815 if options.chrome_rev == constants.CHROME_REV_LOCAL:
816 cros_lib.Die('Chrome root must be set if chrome_rev is %s.' %
817 constants.CHROME_REV_LOCAL)
818
819 if options.chrome_version:
820 if options.chrome_rev != constants.CHROME_REV_SPEC:
821 cros_lib.Die('Chrome rev must be %s if chrome_version is set.' %
822 constants.CHROME_REV_SPEC)
823 else:
824 if options.chrome_rev == constants.CHROME_REV_SPEC:
825 cros_lib.Die('Chrome rev must not be %s if chrome_version is not set.' %
826 constants.CHROME_REV_SPEC)
827
Ryan Cui54da0702012-04-19 18:38:08 -0700828 if options.local and options.remote:
829 cros_lib.Die('Cannot specify both --remote and --local')
830
Ryan Cuicedd8a52012-03-22 02:28:35 -0700831 if options.remote and not (options.gerrit_patches or options.local_patches):
Brian Harring3fec5a82012-03-01 05:57:03 -0800832 cros_lib.Die('Must provide patches when running with --remote.')
833
834 if len(args) > 1 and not options.remote:
835 cros_lib.Die('Multiple configs not supported if not running with --remote.')
836
Ryan Cuiba41ad32012-03-08 17:15:29 -0800837 if options.buildbot and options.remote_trybot:
838 cros_lib.Die('--buildbot and --remote-trybot cannot be used together.')
839
Ryan Cui54da0702012-04-19 18:38:08 -0700840 if options.buildbot and (options.remote or options.local):
841 cros_lib.Die('--remote and --local do not apply when using --buildbot.')
842
Ryan Cui85867972012-02-23 18:21:49 -0800843 # Record whether --debug was set explicitly vs. it was inferred.
844 options.debug_forced = False
845 if options.debug:
846 options.debug_forced = True
847 else:
Ryan Cui16ca5812012-03-08 20:34:27 -0800848 # We don't set debug by default for
849 # 1. --buildbot invocations.
850 # 2. --remote invocations, because it needs to push changes to the tryjob
851 # repo.
852 options.debug = not options.buildbot and not options.remote
Brian Harring3fec5a82012-03-01 05:57:03 -0800853
Brian Harring3fec5a82012-03-01 05:57:03 -0800854
Ryan Cuicedd8a52012-03-22 02:28:35 -0700855def _SplitAndFlatten(appended_items):
856 """Given a list of space-separated items, split into flattened list.
857
858 Given ['abc def', 'hij'] return ['abc', 'def', 'hij'].
859 Arguments:
860 appended_items: List of delimiter-separated items.
861
862 Returns: Flattened list.
863 """
864 new_list = []
865 for item in appended_items:
Mike Frysinger4bd23892012-03-26 15:08:52 -0400866 new_list.extend(item.split())
Ryan Cuicedd8a52012-03-22 02:28:35 -0700867 return new_list
868
869
Ryan Cui85867972012-02-23 18:21:49 -0800870def _PostParseCheck(options, args):
871 """Perform some usage validation after we've parsed the arguments
Brian Harring3fec5a82012-03-01 05:57:03 -0800872
Ryan Cui85867972012-02-23 18:21:49 -0800873 Args:
874 options/args: The options/args object returned by optparse
875 """
876 if not options.resume:
Ryan Cuicedd8a52012-03-22 02:28:35 -0700877 options.gerrit_patches = _SplitAndFlatten(options.gerrit_patches)
878 options.remote_patches = _SplitAndFlatten(options.remote_patches)
Ryan Cui85867972012-02-23 18:21:49 -0800879 try:
880 # TODO(rcui): Split this into two stages, one that parses, another that
881 # validates. Parsing step will be called by _FinishParsing().
Ryan Cuicedd8a52012-03-22 02:28:35 -0700882 options.local_patches = _CheckLocalPatches(
883 _SplitAndFlatten(options.local_patches))
Ryan Cui85867972012-02-23 18:21:49 -0800884 except optparse.OptionValueError as e:
885 cros_lib.Die(str(e))
886
887
888def _ParseCommandLine(parser, argv):
889 """Completely parse the commandline arguments"""
Brian Harring3fec5a82012-03-01 05:57:03 -0800890 (options, args) = parser.parse_args(argv)
891 # Strip out null arguments.
892 # TODO(rcui): Remove when buildbot is fixed
893 args = [arg for arg in args if arg]
Ryan Cui54da0702012-04-19 18:38:08 -0700894 if options.list:
895 _PrintValidConfigs(options.print_all)
896 sys.exit(0)
897
Ryan Cui85867972012-02-23 18:21:49 -0800898 _FinishParsing(options, args)
899 return options, args
900
901
902def main(argv):
903 # Set umask to 022 so files created by buildbot are readable.
904 os.umask(022)
905
906 if cros_lib.IsInsideChroot():
907 cros_lib.Die('Please run cbuildbot from outside the chroot.')
908
909 parser = _CreateParser()
910 (options, args) = _ParseCommandLine(parser, argv)
Brian Harring3fec5a82012-03-01 05:57:03 -0800911
Brian Harring3fec5a82012-03-01 05:57:03 -0800912 _PostParseCheck(options, args)
913
914 if options.remote:
Ryan Cui16ca5812012-03-08 20:34:27 -0800915 cros_lib.DebugLevel.SetDebugLevel(cros_lib.DebugLevel.WARNING)
916
Brian Harring3fec5a82012-03-01 05:57:03 -0800917 # Verify configs are valid.
918 for bot in args:
919 _GetConfig(bot)
920
921 # Verify gerrit patches are valid.
Ryan Cui16ca5812012-03-08 20:34:27 -0800922 print 'Verifying patches...'
Ryan Cuicedd8a52012-03-22 02:28:35 -0700923 _, local_patches = _PreProcessPatches(options.gerrit_patches,
924 options.local_patches)
Ryan Cui16ca5812012-03-08 20:34:27 -0800925 print 'Submitting tryjob...'
Ryan Cuicedd8a52012-03-22 02:28:35 -0700926 tryjob = remote_try.RemoteTryJob(options, args, local_patches)
Ryan Cui39bdbbf2012-02-29 16:15:39 -0800927 tryjob.Submit(testjob=options.test_tryjob, dryrun=options.debug)
Ryan Cui16ca5812012-03-08 20:34:27 -0800928 print 'Tryjob submitted!'
929 print ('Go to %s to view the status of your job.'
Ryan Cui4906e1c2012-04-03 20:09:34 -0700930 % tryjob.GetTrybotWaterfallLink())
Brian Harring3fec5a82012-03-01 05:57:03 -0800931 sys.exit(0)
Ryan Cui54da0702012-04-19 18:38:08 -0700932 elif (not options.buildbot and not options.remote_trybot
933 and not options.resume and not options.local):
934 cros_lib.Warning('Running in LOCAL TRYBOT mode! Use --remote to submit '
935 'REMOTE tryjobs. Use --local to suppress this message.')
936 cros_lib.Warning('Starting April 30th, --local will be required to run the '
937 'local trybot.')
938 time.sleep(5)
Brian Harring3fec5a82012-03-01 05:57:03 -0800939
940 if args:
941 # Only expecting one config
942 bot_id = args[-1]
943 build_config = _GetConfig(bot_id)
944 else:
945 parser.error('Invalid usage. Use -h to see usage.')
946
947 if options.reference_repo is None:
948 repo_path = os.path.join(constants.SOURCE_ROOT, '.repo')
949 # If we're being run from a repo checkout, reuse the repo's git pool to
950 # cut down on sync time.
951 if os.path.exists(repo_path):
952 options.reference_repo = constants.SOURCE_ROOT
953 elif options.reference_repo:
954 if not os.path.exists(options.reference_repo):
955 parser.error('Reference path %s does not exist'
956 % (options.reference_repo,))
957 elif not os.path.exists(os.path.join(options.reference_repo, '.repo')):
958 parser.error('Reference path %s does not look to be the base of a '
959 'repo checkout; no .repo exists in the root.'
960 % (options.reference_repo,))
Ryan Cuid4a24212012-04-04 18:08:12 -0700961
962 if options.buildbot or options.remote_trybot:
Brian Harring470f6112012-03-02 11:47:10 -0800963 if not options.cgroups:
Ryan Cuid4a24212012-04-04 18:08:12 -0700964 parser.error('Options --buildbot/--remote-trybot and --nocgroups cannot '
965 'be used together. Cgroup support is required for '
966 'buildbot/remote-trybot mode.')
Brian Harring470f6112012-03-02 11:47:10 -0800967 if not cgroups.Cgroup.CgroupsSupported():
Ryan Cuid4a24212012-04-04 18:08:12 -0700968 parser.error('Option --buildbot/--remote-trybot was given, but this '
969 'system does not support cgroups. Failing.')
Brian Harring3fec5a82012-03-01 05:57:03 -0800970
Brian Harring351ce442012-03-09 16:38:14 -0800971 missing = []
972 for program in _BUILDBOT_REQUIRED_BINARIES:
973 ret = cros_lib.RunCommand('which %s' % program, shell=True,
974 redirect_stderr=True, redirect_stdout=True,
975 error_code_ok=True, print_cmd=False)
976 if ret.returncode != 0:
977 missing.append(program)
978
979 if missing:
Ryan Cuid4a24212012-04-04 18:08:12 -0700980 parser.error("Option --buildbot/--remote-trybot requires the following "
981 "binaries which couldn't be found in $PATH: %s"
Brian Harring351ce442012-03-09 16:38:14 -0800982 % (', '.join(missing)))
983
Brian Harring3fec5a82012-03-01 05:57:03 -0800984 if options.reference_repo:
985 options.reference_repo = os.path.abspath(options.reference_repo)
986
987 if options.dump_config:
988 # This works, but option ordering is bad...
989 print 'Configuration %s:' % bot_id
990 pretty_printer = pprint.PrettyPrinter(indent=2)
991 pretty_printer.pprint(build_config)
992 sys.exit(0)
993
994 if not options.buildroot:
995 if options.buildbot:
996 parser.error('Please specify a buildroot with the --buildroot option.')
Matt Tennantd55b1f42012-04-13 14:15:01 -0700997
Brian Harring470f6112012-03-02 11:47:10 -0800998 options.buildroot = _DetermineDefaultBuildRoot(build_config['internal'])
999 # We use a marker file in the buildroot to indicate the user has
1000 # consented to using this directory.
1001 if not os.path.exists(repository.GetTrybotMarkerPath(options.buildroot)):
1002 _ConfirmBuildRoot(options.buildroot)
Brian Harring3fec5a82012-03-01 05:57:03 -08001003
1004 # Sanity check of buildroot- specifically that it's not pointing into the
1005 # midst of an existing repo since git-repo doesn't support nesting.
Brian Harring3fec5a82012-03-01 05:57:03 -08001006 if (not repository.IsARepoRoot(options.buildroot) and
David James6b80dc62012-02-29 15:34:40 -08001007 repository.InARepoRepository(options.buildroot)):
Brian Harring3fec5a82012-03-01 05:57:03 -08001008 parser.error('Configured buildroot %s points into a repository checkout, '
1009 'rather than the root of it. This is not supported.'
1010 % options.buildroot)
1011
Brian Harringa184efa2012-03-04 11:51:25 -08001012 with cleanup.EnforcedCleanupSection() as critical_section:
1013 with sudo.SudoKeepAlive():
1014 with cros_lib.AllowDisabling(options.cgroups,
Brian Harring4e6412d2012-03-09 20:54:02 -08001015 cgroups.SimpleContainChildren, 'cbuildbot'):
Brian Harringa184efa2012-03-04 11:51:25 -08001016 # Mark everything between EnforcedCleanupSection and here as having to
1017 # be rolled back via the contextmanager cleanup handlers. This ensures
1018 # that sudo bits cannot outlive cbuildbot, that anything cgroups
1019 # would kill gets killed, etc.
1020 critical_section.ForkWatchdog()
1021
1022 with cros_lib.AllowDisabling(options.timeout > 0,
1023 cros_lib.Timeout, options.timeout):
1024 if not options.buildbot:
1025 build_config = cbuildbot_config.OverrideConfigForTrybot(
Ryan Cui3d6b4742012-03-14 11:42:24 -07001026 build_config,
1027 options.remote_trybot)
Brian Harringa184efa2012-03-04 11:51:25 -08001028
1029 _RunBuildStagesWrapper(options, build_config)