blob: b4a45c7459df1bf5a0f637c24fd22994beee0d49 [file] [log] [blame]
Brian Harring3fec5a82012-03-01 05:57:03 -08001#!/usr/bin/python
2
Mike Frysingerd6925b52012-07-16 16:11:00 -04003# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Brian Harring3fec5a82012-03-01 05:57:03 -08004# 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
Chris Sosa4f6ffaf2012-05-01 17:05:44 -070015import logging
David James58e0c092012-03-04 20:31:12 -080016import multiprocessing
Brian Harring3fec5a82012-03-01 05:57:03 -080017import optparse
18import os
19import pprint
20import sys
Ryan Cui54da0702012-04-19 18:38:08 -070021import time
Brian Harring3fec5a82012-03-01 05:57:03 -080022
23from chromite.buildbot import builderstage as bs
24from chromite.buildbot import cbuildbot_background as background
25from chromite.buildbot import cbuildbot_config
26from chromite.buildbot import cbuildbot_stages as stages
27from chromite.buildbot import cbuildbot_results as results_lib
Brian Harring3fec5a82012-03-01 05:57:03 -080028from chromite.buildbot import constants
29from chromite.buildbot import gerrit_helper
30from chromite.buildbot import patch as cros_patch
31from chromite.buildbot import remote_try
32from chromite.buildbot import repository
33from chromite.buildbot import tee
Ryan Cui16d9e1f2012-05-11 10:50:18 -070034from chromite.buildbot import trybot_patch_pool
Brian Harring3fec5a82012-03-01 05:57:03 -080035
Brian Harringc92a7012012-02-29 10:11:34 -080036from chromite.lib import cgroups
Brian Harringa184efa2012-03-04 11:51:25 -080037from chromite.lib import cleanup
Brian Harring1b8c4c82012-05-29 23:03:04 -070038from chromite.lib import cros_build_lib
Brian Harringaf019fb2012-05-10 15:06:13 -070039from chromite.lib import osutils
Brian Harring3fec5a82012-03-01 05:57:03 -080040from chromite.lib import sudo
41
Ryan Cuiadd49122012-03-21 22:19:58 -070042
Brian Harring1b8c4c82012-05-29 23:03:04 -070043cros_build_lib.STRICT_SUDO = True
Brian Harring3fec5a82012-03-01 05:57:03 -080044
45_DEFAULT_LOG_DIR = 'cbuildbot_logs'
46_BUILDBOT_LOG_FILE = 'cbuildbot.log'
47_DEFAULT_EXT_BUILDROOT = 'trybot'
48_DEFAULT_INT_BUILDROOT = 'trybot-internal'
Brian Harring3fec5a82012-03-01 05:57:03 -080049_DISTRIBUTED_TYPES = [constants.COMMIT_QUEUE_TYPE, constants.PFQ_TYPE,
50 constants.CANARY_TYPE, constants.CHROME_PFQ_TYPE,
51 constants.PALADIN_TYPE]
Brian Harring351ce442012-03-09 16:38:14 -080052_BUILDBOT_REQUIRED_BINARIES = ('pbzip2',)
Brian Harring3fec5a82012-03-01 05:57:03 -080053
Brian Harring37e559b2012-05-22 20:47:32 -070054# Used by --resume and --bootstrap to decipher which options they
55# can pass to the target cbuildbot (since it may not have that
56# option).
57# Format is Major:Minor. Minor is used for tracking new options added
58# that aren't critical to the older version if it's not ran.
59# Major is used for tracking heavy API breakage- for example, no longer
60# supporting the --resume option.
61_REEXEC_API_MAJOR = 0
62_REEXEC_API_MINOR = 1
63_REEXEC_API_VERSION = '%i.%i' % (_REEXEC_API_MAJOR, _REEXEC_API_MINOR)
64
Brian Harring3fec5a82012-03-01 05:57:03 -080065
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070066def _PrintValidConfigs(display_all=False):
Brian Harring3fec5a82012-03-01 05:57:03 -080067 """Print a list of valid buildbot configs.
68
69 Arguments:
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070070 display_all: Print all configs. Otherwise, prints only configs with
71 trybot_list=True.
Brian Harring3fec5a82012-03-01 05:57:03 -080072 """
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070073 def _GetSortKey(config_name):
74 config_dict = cbuildbot_config.config[config_name]
75 return (not config_dict['trybot_list'], config_dict['description'],
76 config_name)
77
Brian Harring3fec5a82012-03-01 05:57:03 -080078 COLUMN_WIDTH = 45
79 print 'config'.ljust(COLUMN_WIDTH), 'description'
80 print '------'.ljust(COLUMN_WIDTH), '-----------'
81 config_names = cbuildbot_config.config.keys()
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070082 config_names.sort(key=_GetSortKey)
Brian Harring3fec5a82012-03-01 05:57:03 -080083 for name in config_names:
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070084 if display_all or cbuildbot_config.config[name]['trybot_list']:
85 desc = cbuildbot_config.config[name].get('description')
86 desc = desc if desc else ''
Brian Harring3fec5a82012-03-01 05:57:03 -080087 print name.ljust(COLUMN_WIDTH), desc
88
89
90def _GetConfig(config_name):
91 """Gets the configuration for the build"""
92 if not cbuildbot_config.config.has_key(config_name):
93 print 'Non-existent configuration %s specified.' % config_name
94 print 'Please specify one of:'
95 _PrintValidConfigs()
96 sys.exit(1)
97
98 result = cbuildbot_config.config[config_name]
99
100 return result
101
102
Ryan Cuie1e4e662012-05-21 16:39:46 -0700103def AcquirePoolFromOptions(options):
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700104 """Generate patch objects from passed in options.
Brian Harring3fec5a82012-03-01 05:57:03 -0800105
106 Args:
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700107 options: The options object generated by optparse.
Brian Harring3fec5a82012-03-01 05:57:03 -0800108
Ryan Cuif7f24692012-05-18 16:35:33 -0700109 Returns:
110 trybot_patch_pool.TrybotPatchPool object.
111
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700112 Raises:
113 gerrit_helper.GerritException, cros_patch.PatchException
Brian Harring3fec5a82012-03-01 05:57:03 -0800114 """
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700115 gerrit_patches = []
116 local_patches = []
117 remote_patches = []
Brian Harring3fec5a82012-03-01 05:57:03 -0800118
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700119 if options.gerrit_patches:
120 gerrit_patches = gerrit_helper.GetGerritPatchInfo(
121 options.gerrit_patches)
122 for patch in gerrit_patches:
123 if patch.IsAlreadyMerged():
Brian Harring1b8c4c82012-05-29 23:03:04 -0700124 cros_build_lib.Warning('Patch %s has already been merged.' % str(patch))
Brian Harring3fec5a82012-03-01 05:57:03 -0800125
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700126 if options.local_patches:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700127 manifest = cros_build_lib.ManifestCheckout.Cached(options.sourceroot)
Brian Harring609dc4e2012-05-07 02:17:44 -0700128 local_patches = cros_patch.PrepareLocalPatches(manifest,
129 options.local_patches)
Brian Harring3fec5a82012-03-01 05:57:03 -0800130
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700131 if options.remote_patches:
132 remote_patches = cros_patch.PrepareRemotePatches(
133 options.remote_patches)
Brian Harring3fec5a82012-03-01 05:57:03 -0800134
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700135 return trybot_patch_pool.TrybotPatchPool(gerrit_patches, local_patches,
136 remote_patches)
Brian Harring3fec5a82012-03-01 05:57:03 -0800137
138
Brian Harring3fec5a82012-03-01 05:57:03 -0800139class Builder(object):
140 """Parent class for all builder types.
141
142 This class functions as a parent class for various build types. It's intended
143 use is builder_instance.Run().
144
145 Vars:
Brian Harring3fec5a82012-03-01 05:57:03 -0800146 build_config: The configuration dictionary from cbuildbot_config.
147 options: The options provided from optparse in main().
Ryan Cui5616a512012-08-17 13:39:36 -0700148 archive_urls: Where our artifacts for this builder will be archived.
Brian Harring3fec5a82012-03-01 05:57:03 -0800149 release_tag: The associated "chrome os version" of this build.
Brian Harring3fec5a82012-03-01 05:57:03 -0800150 """
151
Ryan Cuie1e4e662012-05-21 16:39:46 -0700152 def __init__(self, options, build_config):
Brian Harring3fec5a82012-03-01 05:57:03 -0800153 """Initializes instance variables. Must be called by all subclasses."""
Brian Harring3fec5a82012-03-01 05:57:03 -0800154 self.build_config = build_config
155 self.options = options
156
157 # TODO, Remove here and in config after bug chromium-os:14649 is fixed.
158 if self.build_config['chromeos_official']:
159 os.environ['CHROMEOS_OFFICIAL'] = '1'
160
David James58e0c092012-03-04 20:31:12 -0800161 self.archive_stages = {}
Brian Harring3fec5a82012-03-01 05:57:03 -0800162 self.archive_urls = {}
163 self.release_tag = None
Brian Harring76d1bf62012-06-01 13:52:48 -0700164 self.patch_pool = trybot_patch_pool.TrybotPatchPool()
Brian Harring3fec5a82012-03-01 05:57:03 -0800165
Ryan Cuie1e4e662012-05-21 16:39:46 -0700166 bs.BuilderStage.SetManifestBranch(self.options.branch)
Ryan Cuif7f24692012-05-18 16:35:33 -0700167
Brian Harring3fec5a82012-03-01 05:57:03 -0800168 def Initialize(self):
169 """Runs through the initialization steps of an actual build."""
Ryan Cuif7f24692012-05-18 16:35:33 -0700170 if self.options.resume:
171 results_lib.LoadCheckpoint(self.options.buildroot)
Brian Harring3fec5a82012-03-01 05:57:03 -0800172
Brian Harring3fec5a82012-03-01 05:57:03 -0800173 self._RunStage(stages.CleanUpStage)
174
175 def _GetStageInstance(self, stage, *args, **kwargs):
176 """Helper function to get an instance given the args.
177
David James944a48e2012-03-07 12:19:03 -0800178 Useful as almost all stages just take in options and build_config.
Brian Harring3fec5a82012-03-01 05:57:03 -0800179 """
David James944a48e2012-03-07 12:19:03 -0800180 config = kwargs.pop('config', self.build_config)
181 return stage(self.options, config, *args, **kwargs)
Brian Harring3fec5a82012-03-01 05:57:03 -0800182
183 def _SetReleaseTag(self):
184 """Sets the release tag from the manifest_manager.
185
186 Must be run after sync stage as syncing enables us to have a release tag.
187 """
188 # Extract version we have decided to build into self.release_tag.
189 manifest_manager = stages.ManifestVersionedSyncStage.manifest_manager
190 if manifest_manager:
191 self.release_tag = manifest_manager.current_version
192
193 def _RunStage(self, stage, *args, **kwargs):
194 """Wrapper to run a stage."""
195 stage_instance = self._GetStageInstance(stage, *args, **kwargs)
196 return stage_instance.Run()
197
198 def GetSyncInstance(self):
199 """Returns an instance of a SyncStage that should be run.
200
201 Subclasses must override this method.
202 """
203 raise NotImplementedError()
204
205 def RunStages(self):
206 """Subclasses must override this method. Runs the appropriate code."""
207 raise NotImplementedError()
208
Brian Harring3fec5a82012-03-01 05:57:03 -0800209 def _ShouldReExecuteInBuildRoot(self):
210 """Returns True if this build should be re-executed in the buildroot."""
211 abs_buildroot = os.path.abspath(self.options.buildroot)
212 return not os.path.abspath(__file__).startswith(abs_buildroot)
213
214 def _ReExecuteInBuildroot(self, sync_instance):
215 """Reexecutes self in buildroot and returns True if build succeeds.
216
217 This allows the buildbot code to test itself when changes are patched for
218 buildbot-related code. This is a no-op if the buildroot == buildroot
219 of the running chromite checkout.
220
221 Args:
222 sync_instance: Instance of the sync stage that was run to sync.
223
224 Returns:
225 True if the Build succeeded.
226 """
Brian Harring3fec5a82012-03-01 05:57:03 -0800227 if not self.options.resume:
Ryan Cuif7f24692012-05-18 16:35:33 -0700228 results_lib.WriteCheckpoint(self.options.buildroot)
Brian Harring3fec5a82012-03-01 05:57:03 -0800229
Brian Harring37e559b2012-05-22 20:47:32 -0700230 # Get the re-exec API version of the target chromite; if it's incompatible
231 # with us, bail now.
Brian Harring1b8c4c82012-05-29 23:03:04 -0700232 api = cros_build_lib.RunCommandCaptureOutput(
Brian Harring37e559b2012-05-22 20:47:32 -0700233 [constants.PATH_TO_CBUILDBOT] + ['--reexec-api-version'],
234 cwd=self.options.buildroot, error_code_ok=True)
235 # If the command failed, then we're targeting a cbuildbot that lacks the
236 # option; assume 0:0 (ie, initial state).
237 major, minor = 0, 0
238 if api.returncode == 0:
239 major, minor = map(int, api.output.strip().split('.', 1))
240
241 if major != _REEXEC_API_MAJOR:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700242 cros_build_lib.Die(
Brian Harring37e559b2012-05-22 20:47:32 -0700243 'The targeted version of chromite in buildroot %s requires '
244 'api version %i, but we are api version %i. We cannot proceed.'
245 % (self.options.buildroot, major, _REEXEC_API_MAJOR))
246
Brian Harring3fec5a82012-03-01 05:57:03 -0800247 # Re-write paths to use absolute paths.
248 # Suppress any timeout options given from the commandline in the
249 # invoked cbuildbot; our timeout will enforce it instead.
Brian Harringf11bf682012-05-14 15:53:43 -0700250 args_to_append = ['--resume', '--timeout', '0', '--notee', '--nocgroups',
251 '--buildroot', os.path.abspath(self.options.buildroot)]
Brian Harring3fec5a82012-03-01 05:57:03 -0800252
253 if self.options.chrome_root:
254 args_to_append += ['--chrome_root',
255 os.path.abspath(self.options.chrome_root)]
256
257 if stages.ManifestVersionedSyncStage.manifest_manager:
258 ver = stages.ManifestVersionedSyncStage.manifest_manager.current_version
259 args_to_append += ['--version', ver]
260
261 if isinstance(sync_instance, stages.CommitQueueSyncStage):
262 vp_file = sync_instance.SaveValidationPool()
263 args_to_append += ['--validation_pool', vp_file]
264
265 # Re-run the command in the buildroot.
266 # Finally, be generous and give the invoked cbuildbot 30s to shutdown
267 # when something occurs. It should exit quicker, but the sigterm may
268 # hit while the system is particularly busy.
Brian Harring1b8c4c82012-05-29 23:03:04 -0700269 return_obj = cros_build_lib.RunCommand(
Ryan Cuif7f24692012-05-18 16:35:33 -0700270 [constants.PATH_TO_CBUILDBOT] + sys.argv[1:] + args_to_append,
Brian Harring3fec5a82012-03-01 05:57:03 -0800271 cwd=self.options.buildroot, error_code_ok=True, kill_timeout=30)
272 return return_obj.returncode == 0
273
Ryan Cuif7f24692012-05-18 16:35:33 -0700274 def _InitializeTrybotPatchPool(self):
275 """Generate patch pool from patches specified on the command line.
276
277 Do this only if we need to patch changes later on.
278 """
279 changes_stage = stages.PatchChangesStage.StageNamePrefix()
280 check_func = results_lib.Results.PreviouslyCompletedRecord
281 if not check_func(changes_stage) or self.options.bootstrap:
Ryan Cuie1e4e662012-05-21 16:39:46 -0700282 self.patch_pool = AcquirePoolFromOptions(self.options)
Ryan Cuif7f24692012-05-18 16:35:33 -0700283
284 def _GetBootstrapStage(self):
285 """Constructs and returns the BootStrapStage object.
286
287 We return None when there are no chromite patches to test, and
288 --test-bootstrap wasn't passed in.
289 """
290 stage = None
291 chromite_pool = self.patch_pool.Filter(project=constants.CHROMITE_PROJECT)
Ryan Cui5616a512012-08-17 13:39:36 -0700292 manifest_pool = self.patch_pool.FilterManifest()
Chris Sosa126103a2012-06-18 09:03:17 -0700293 chromite_branch = cros_build_lib.GetChromiteTrackingBranch()
Ryan Cui5616a512012-08-17 13:39:36 -0700294 if (chromite_pool or manifest_pool or self.options.test_bootstrap
Ryan Cuie1e4e662012-05-21 16:39:46 -0700295 or chromite_branch != self.options.branch):
Ryan Cuif7f24692012-05-18 16:35:33 -0700296 stage = stages.BootstrapStage(self.options, self.build_config,
Ryan Cui5616a512012-08-17 13:39:36 -0700297 chromite_pool, manifest_pool)
Ryan Cuif7f24692012-05-18 16:35:33 -0700298 return stage
299
Brian Harring3fec5a82012-03-01 05:57:03 -0800300 def Run(self):
Ryan Cuif7f24692012-05-18 16:35:33 -0700301 """Main runner for this builder class. Runs build and prints summary.
302
303 Returns:
304 Whether the build succeeded.
305 """
306 self._InitializeTrybotPatchPool()
307
308 if self.options.bootstrap:
309 bootstrap_stage = self._GetBootstrapStage()
310 if bootstrap_stage:
311 # BootstrapStage blocks on re-execution of cbuildbot.
312 bootstrap_stage.Run()
313 return bootstrap_stage.returncode == 0
314
Brian Harring3fec5a82012-03-01 05:57:03 -0800315 print_report = True
David James3d4d3502012-04-09 15:12:06 -0700316 exception_thrown = False
Brian Harring3fec5a82012-03-01 05:57:03 -0800317 success = True
318 try:
319 self.Initialize()
320 sync_instance = self.GetSyncInstance()
321 sync_instance.Run()
322 self._SetReleaseTag()
323
Ryan Cui967f7cc2012-08-17 13:01:12 -0700324 # Filter out patches to manifest, since PatchChangesStage can't handle
325 # them. Manifest patches are patched in the BootstrapStage.
326 non_manifest_patches = self.patch_pool.FilterManifest(negate=True)
327 if non_manifest_patches:
328 self._RunStage(stages.PatchChangesStage, non_manifest_patches)
Brian Harring3fec5a82012-03-01 05:57:03 -0800329
330 if self._ShouldReExecuteInBuildRoot():
331 print_report = False
332 success = self._ReExecuteInBuildroot(sync_instance)
333 else:
334 self.RunStages()
David James7fbf2d42012-07-14 18:23:49 -0700335 except results_lib.StepFailure:
336 # StepFailure exceptions are already recorded in the report, so there
337 # is no need to print these tracebacks twice.
338 exception_thrown = True
339 if not print_report:
340 raise
David James3d4d3502012-04-09 15:12:06 -0700341 except Exception:
342 exception_thrown = True
343 raise
Brian Harring3fec5a82012-03-01 05:57:03 -0800344 finally:
345 if print_report:
Ryan Cuif7f24692012-05-18 16:35:33 -0700346 results_lib.WriteCheckpoint(self.options.buildroot)
Brian Harring3fec5a82012-03-01 05:57:03 -0800347 print '\n\n\n@@@BUILD_STEP Report@@@\n'
348 results_lib.Results.Report(sys.stdout, self.archive_urls,
349 self.release_tag)
350 success = results_lib.Results.BuildSucceededSoFar()
David James3d4d3502012-04-09 15:12:06 -0700351 if exception_thrown and success:
352 success = False
David Jamesbb20ac82012-07-18 10:59:16 -0700353 cros_build_lib.PrintBuildbotStepWarnings()
354 print """\
David James3d4d3502012-04-09 15:12:06 -0700355Exception thrown, but all stages marked successful. This is an internal error,
356because the stage that threw the exception should be marked as failing."""
Brian Harring3fec5a82012-03-01 05:57:03 -0800357
358 return success
359
360
361class SimpleBuilder(Builder):
362 """Builder that performs basic vetting operations."""
363
364 def GetSyncInstance(self):
365 """Sync to lkgm or TOT as necessary.
366
367 Returns: the instance of the sync stage that was run.
368 """
369 if self.options.lkgm or self.build_config['use_lkgm']:
370 sync_stage = self._GetStageInstance(stages.LKGMSyncStage)
371 else:
372 sync_stage = self._GetStageInstance(stages.SyncStage)
373
374 return sync_stage
375
David James58e0c092012-03-04 20:31:12 -0800376 def _RunBackgroundStagesForBoard(self, board):
377 """Run background board-specific stages for the specified board."""
David James58e0c092012-03-04 20:31:12 -0800378 archive_stage = self.archive_stages[board]
David James944a48e2012-03-07 12:19:03 -0800379 configs = self.build_config['board_specific_configs']
380 config = configs.get(board, self.build_config)
381 stage_list = [[stages.VMTestStage, board, archive_stage],
382 [stages.ChromeTestStage, board, archive_stage],
383 [stages.UnitTestStage, board],
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700384 [stages.UploadPrebuiltsStage, board, archive_stage]]
Brian Harring3fec5a82012-03-01 05:57:03 -0800385
David James58e0c092012-03-04 20:31:12 -0800386 # We can not run hw tests without archiving the payloads.
387 if self.options.archive:
David James944a48e2012-03-07 12:19:03 -0800388 for suite in config['hw_tests']:
Peter Mayo85e94372012-08-17 12:42:14 -0400389 stage_list.append([stages.HWTestStage, board, archive_stage, suite])
Chris Sosab50dc932012-03-01 14:00:58 -0800390
Chris Sosa817b1f92012-07-19 15:00:23 -0700391 for suite in config['async_hw_tests']:
392 stage_list.append([stages.ASyncHWTestStage, board, archive_stage,
393 suite])
394
David James944a48e2012-03-07 12:19:03 -0800395 steps = [self._GetStageInstance(*x, config=config).Run for x in stage_list]
396 background.RunParallelSteps(steps + [archive_stage.Run])
Brian Harring3fec5a82012-03-01 05:57:03 -0800397
398 def RunStages(self):
399 """Runs through build process."""
400 self._RunStage(stages.BuildBoardStage)
401
402 # TODO(sosa): Split these out into classes.
Brian Harring3fec5a82012-03-01 05:57:03 -0800403 if self.build_config['build_type'] == constants.CHROOT_BUILDER_TYPE:
Zdenek Behan62a57792012-08-31 15:09:08 +0200404 self._RunStage(stages.SDKPackageStage)
Brian Harring3fec5a82012-03-01 05:57:03 -0800405 self._RunStage(stages.SDKTestStage)
406 self._RunStage(stages.UploadPrebuiltsStage,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700407 constants.CHROOT_BUILDER_BOARD, None)
Brian Harring3fec5a82012-03-01 05:57:03 -0800408 elif self.build_config['build_type'] == constants.REFRESH_PACKAGES_TYPE:
409 self._RunStage(stages.RefreshPackageStatusStage)
410 else:
411 self._RunStage(stages.UprevStage)
Brian Harring3fec5a82012-03-01 05:57:03 -0800412
David James944a48e2012-03-07 12:19:03 -0800413 configs = self.build_config['board_specific_configs']
David James58e0c092012-03-04 20:31:12 -0800414 for board in self.build_config['boards']:
David James944a48e2012-03-07 12:19:03 -0800415 config = configs.get(board, self.build_config)
416 archive_stage = self._GetStageInstance(stages.ArchiveStage, board,
417 config=config)
David James58e0c092012-03-04 20:31:12 -0800418 self.archive_stages[board] = archive_stage
419
David James944a48e2012-03-07 12:19:03 -0800420 # Set up a process pool to run test/archive stages in the background.
421 # This process runs task(board) for each board added to the queue.
David James58e0c092012-03-04 20:31:12 -0800422 queue = multiprocessing.Queue()
423 task = self._RunBackgroundStagesForBoard
424 with background.BackgroundTaskRunner(queue, task):
David James944a48e2012-03-07 12:19:03 -0800425 for board in self.build_config['boards']:
David James58e0c092012-03-04 20:31:12 -0800426 # Run BuildTarget in the foreground.
David James944a48e2012-03-07 12:19:03 -0800427 archive_stage = self.archive_stages[board]
428 config = configs.get(board, self.build_config)
429 self._RunStage(stages.BuildTargetStage, board, archive_stage,
Chris Sosa1a87b3e2012-04-12 13:20:42 -0700430 self.release_tag, config=config)
David James58e0c092012-03-04 20:31:12 -0800431 self.archive_urls[board] = archive_stage.GetDownloadUrl()
432
David James944a48e2012-03-07 12:19:03 -0800433 # Kick off task(board) in the background.
David James58e0c092012-03-04 20:31:12 -0800434 queue.put([board])
435
Brian Harring3fec5a82012-03-01 05:57:03 -0800436
437class DistributedBuilder(SimpleBuilder):
438 """Build class that has special logic to handle distributed builds.
439
440 These builds sync using git/manifest logic in manifest_versions. In general
441 they use a non-distributed builder code for the bulk of the work.
442 """
Ryan Cuif7f24692012-05-18 16:35:33 -0700443 def __init__(self, *args, **kwargs):
Brian Harring3fec5a82012-03-01 05:57:03 -0800444 """Initializes a buildbot builder.
445
446 Extra variables:
447 completion_stage_class: Stage used to complete a build. Set in the Sync
448 stage.
449 """
Ryan Cuif7f24692012-05-18 16:35:33 -0700450 super(DistributedBuilder, self).__init__(*args, **kwargs)
Brian Harring3fec5a82012-03-01 05:57:03 -0800451 self.completion_stage_class = None
452
453 def GetSyncInstance(self):
454 """Syncs the tree using one of the distributed sync logic paths.
455
456 Returns: the instance of the sync stage that was run.
457 """
458 # Determine sync class to use. CQ overrides PFQ bits so should check it
459 # first.
460 if cbuildbot_config.IsCQType(self.build_config['build_type']):
461 sync_stage = self._GetStageInstance(stages.CommitQueueSyncStage)
462 self.completion_stage_class = stages.CommitQueueCompletionStage
463 elif cbuildbot_config.IsPFQType(self.build_config['build_type']):
464 sync_stage = self._GetStageInstance(stages.LKGMCandidateSyncStage)
465 self.completion_stage_class = stages.LKGMCandidateSyncCompletionStage
466 else:
467 sync_stage = self._GetStageInstance(stages.ManifestVersionedSyncStage)
468 self.completion_stage_class = stages.ManifestVersionedSyncCompletionStage
469
470 return sync_stage
471
472 def Publish(self, was_build_successful):
473 """Completes build by publishing any required information."""
474 completion_stage = self._GetStageInstance(self.completion_stage_class,
475 was_build_successful)
476 completion_stage.Run()
477 name = completion_stage.name
478 if not results_lib.Results.WasStageSuccessful(name):
479 should_publish_changes = False
480 else:
481 should_publish_changes = (self.build_config['master'] and
482 was_build_successful)
483
484 if should_publish_changes:
485 self._RunStage(stages.PublishUprevChangesStage)
486
487 def RunStages(self):
488 """Runs simple builder logic and publishes information to overlays."""
489 was_build_successful = False
490 try:
David Jamesf55709e2012-03-13 09:10:15 -0700491 super(DistributedBuilder, self).RunStages()
492 was_build_successful = results_lib.Results.BuildSucceededSoFar()
Brian Harring3fec5a82012-03-01 05:57:03 -0800493 except SystemExit as ex:
494 # If a stage calls sys.exit(0), it's exiting with success, so that means
495 # we should mark ourselves as successful.
496 if ex.code == 0:
497 was_build_successful = True
498 raise
499 finally:
500 self.Publish(was_build_successful)
501
Brian Harring3fec5a82012-03-01 05:57:03 -0800502
503def _ConfirmBuildRoot(buildroot):
504 """Confirm with user the inferred buildroot, and mark it as confirmed."""
505 warning = 'Using default directory %s as buildroot' % buildroot
Brian Harring1b8c4c82012-05-29 23:03:04 -0700506 response = cros_build_lib.YesNoPrompt(
507 default=cros_build_lib.NO, warning=warning, full=True)
508 if response == cros_build_lib.NO:
Brian Harring3fec5a82012-03-01 05:57:03 -0800509 print('Please specify a buildroot with the --buildroot option.')
510 sys.exit(0)
511
512 if not os.path.exists(buildroot):
513 os.mkdir(buildroot)
514
515 repository.CreateTrybotMarker(buildroot)
516
517
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700518def _ConfirmRemoteBuildbotRun():
519 """Confirm user wants to run with --buildbot --remote."""
520 warning = ('You are about to launch a PRODUCTION job! This is *NOT* a '
521 'trybot run! Are you sure?')
Brian Harring1b8c4c82012-05-29 23:03:04 -0700522 response = cros_build_lib.YesNoPrompt(
523 default=cros_build_lib.NO, warning=warning, full=True)
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700524
Brian Harring1b8c4c82012-05-29 23:03:04 -0700525 if response == cros_build_lib.NO:
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700526 print('Please specify --pass-through="--debug".')
527 sys.exit(0)
528
529
Ryan Cui5ba7e152012-05-10 14:36:52 -0700530def _DetermineDefaultBuildRoot(sourceroot, internal_build):
Brian Harring3fec5a82012-03-01 05:57:03 -0800531 """Default buildroot to be under the directory that contains current checkout.
532
533 Arguments:
534 internal_build: Whether the build is an internal build
Ryan Cui5ba7e152012-05-10 14:36:52 -0700535 sourceroot: Use specified sourceroot.
Brian Harring3fec5a82012-03-01 05:57:03 -0800536 """
Ryan Cui5ba7e152012-05-10 14:36:52 -0700537 if not repository.IsARepoRoot(sourceroot):
Brian Harring1b8c4c82012-05-29 23:03:04 -0700538 cros_build_lib.Die(
539 'Could not find root of local checkout at %s. Please specify '
540 'using the --sourceroot option.' % sourceroot)
Brian Harring3fec5a82012-03-01 05:57:03 -0800541
542 # Place trybot buildroot under the directory containing current checkout.
Ryan Cui5ba7e152012-05-10 14:36:52 -0700543 top_level = os.path.dirname(os.path.realpath(sourceroot))
Brian Harring3fec5a82012-03-01 05:57:03 -0800544 if internal_build:
545 buildroot = os.path.join(top_level, _DEFAULT_INT_BUILDROOT)
546 else:
547 buildroot = os.path.join(top_level, _DEFAULT_EXT_BUILDROOT)
548
549 return buildroot
550
551
552def _BackupPreviousLog(log_file, backup_limit=25):
553 """Rename previous log.
554
555 Args:
556 log_file: The absolute path to the previous log.
557 """
558 if os.path.exists(log_file):
559 old_logs = sorted(glob.glob(log_file + '.*'),
560 key=distutils.version.LooseVersion)
561
562 if len(old_logs) >= backup_limit:
563 os.remove(old_logs[0])
564
565 last = 0
566 if old_logs:
567 last = int(old_logs.pop().rpartition('.')[2])
568
569 os.rename(log_file, log_file + '.' + str(last + 1))
570
Ryan Cui5616a512012-08-17 13:39:36 -0700571
David James944a48e2012-03-07 12:19:03 -0800572def _RunBuildStagesWrapper(options, build_config):
Brian Harring3fec5a82012-03-01 05:57:03 -0800573 """Helper function that wraps RunBuildStages()."""
574 def IsDistributedBuilder():
575 """Determines whether the build_config should be a DistributedBuilder."""
576 if not options.buildbot:
577 return False
578 elif build_config['build_type'] in _DISTRIBUTED_TYPES:
579 chrome_rev = build_config['chrome_rev']
580 if options.chrome_rev: chrome_rev = options.chrome_rev
581 # We don't do distributed logic to TOT Chrome PFQ's, nor local
582 # chrome roots (e.g. chrome try bots)
583 if chrome_rev not in [constants.CHROME_REV_TOT,
584 constants.CHROME_REV_LOCAL,
585 constants.CHROME_REV_SPEC]:
586 return True
587
588 return False
589
Brian Harring1b8c4c82012-05-29 23:03:04 -0700590 cros_build_lib.Info("cbuildbot executed with args %s"
591 % ' '.join(map(repr, sys.argv)))
Brian Harring3fec5a82012-03-01 05:57:03 -0800592
Ryan Cuif7f24692012-05-18 16:35:33 -0700593 target = DistributedBuilder if IsDistributedBuilder() else SimpleBuilder
Ryan Cuie1e4e662012-05-21 16:39:46 -0700594 buildbot = target(options, build_config)
Brian Harringd166aaf2012-05-14 18:31:53 -0700595 if not buildbot.Run():
596 sys.exit(1)
Brian Harring3fec5a82012-03-01 05:57:03 -0800597
598
599# Parser related functions
Ryan Cui5ba7e152012-05-10 14:36:52 -0700600def _CheckLocalPatches(sourceroot, local_patches):
Brian Harring3fec5a82012-03-01 05:57:03 -0800601 """Do an early quick check of the passed-in patches.
602
603 If the branch of a project is not specified we append the current branch the
604 project is on.
Ryan Cui5ba7e152012-05-10 14:36:52 -0700605
606 Args:
607 sourceroot: The checkout where patches are coming from.
Brian Harring3fec5a82012-03-01 05:57:03 -0800608 """
Ryan Cuicedd8a52012-03-22 02:28:35 -0700609 verified_patches = []
Brian Harring1b8c4c82012-05-29 23:03:04 -0700610 manifest = cros_build_lib.ManifestCheckout.Cached(sourceroot)
Ryan Cuicedd8a52012-03-22 02:28:35 -0700611 for patch in local_patches:
Brian Harring3fec5a82012-03-01 05:57:03 -0800612 components = patch.split(':')
613 if len(components) > 2:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700614 cros_build_lib.Die(
615 'Specify local patches in project[:branch] format. Got %s' % patch)
Brian Harring3fec5a82012-03-01 05:57:03 -0800616
617 # validate project
618 project = components[0]
Brian Harring3fec5a82012-03-01 05:57:03 -0800619
Brian Harring609dc4e2012-05-07 02:17:44 -0700620 try:
621 project_dir = manifest.GetProjectPath(project, True)
622 except KeyError:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700623 cros_build_lib.Die('Project %s does not exist.' % project)
Brian Harring3fec5a82012-03-01 05:57:03 -0800624
625 # If no branch was specified, we use the project's current branch.
626 if len(components) == 1:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700627 branch = cros_build_lib.GetCurrentBranch(project_dir)
Brian Harring3fec5a82012-03-01 05:57:03 -0800628 if not branch:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700629 cros_build_lib.Die('Project %s is not on a branch!' % project)
Brian Harring3fec5a82012-03-01 05:57:03 -0800630 else:
631 branch = components[1]
Brian Harring1b8c4c82012-05-29 23:03:04 -0700632 if not cros_build_lib.DoesLocalBranchExist(project_dir, branch):
633 cros_build_lib.Die('Project %s does not have branch %s'
634 % (project, branch))
Brian Harring3fec5a82012-03-01 05:57:03 -0800635
Brian Harring609dc4e2012-05-07 02:17:44 -0700636 verified_patches.append('%s:%s' % (project, branch))
Brian Harring3fec5a82012-03-01 05:57:03 -0800637
Ryan Cuicedd8a52012-03-22 02:28:35 -0700638 return verified_patches
Brian Harring3fec5a82012-03-01 05:57:03 -0800639
640
Brian Harring3fec5a82012-03-01 05:57:03 -0800641def _CheckChromeVersionOption(_option, _opt_str, value, parser):
642 """Upgrade other options based on chrome_version being passed."""
643 value = value.strip()
644
645 if parser.values.chrome_rev is None and value:
646 parser.values.chrome_rev = constants.CHROME_REV_SPEC
647
648 parser.values.chrome_version = value
649
650
651def _CheckChromeRootOption(_option, _opt_str, value, parser):
652 """Validate and convert chrome_root to full-path form."""
Brian Harring3fec5a82012-03-01 05:57:03 -0800653 if parser.values.chrome_rev is None:
654 parser.values.chrome_rev = constants.CHROME_REV_LOCAL
655
Ryan Cui5ba7e152012-05-10 14:36:52 -0700656 parser.values.chrome_root = value
Brian Harring3fec5a82012-03-01 05:57:03 -0800657
658
659def _CheckChromeRevOption(_option, _opt_str, value, parser):
660 """Validate the chrome_rev option."""
661 value = value.strip()
662 if value not in constants.VALID_CHROME_REVISIONS:
663 raise optparse.OptionValueError('Invalid chrome rev specified')
664
665 parser.values.chrome_rev = value
666
667
Ryan Cuif37608e2012-07-10 14:28:48 -0700668def _CheckGerritChromeOption(_option, _opt_str, value, parser):
669 """Validate the chrome_rev option."""
670 if parser.values.chrome_rev is None:
671 parser.values.chrome_rev = constants.CHROME_REV_TOT
672
673 parser.values.gerrit_chrome = True
674
675
Ryan Cui5ba7e152012-05-10 14:36:52 -0700676class CustomParser(optparse.OptionParser):
677 def add_remote_option(self, *args, **kwargs):
678 """For arguments that are passed-through to remote trybot."""
679 return optparse.OptionParser.add_option(self, *args,
680 remote_pass_through=True,
681 **kwargs)
682
683
684class CustomGroup(optparse.OptionGroup):
685 def add_remote_option(self, *args, **kwargs):
686 """For arguments that are passed-through to remote trybot."""
687 return optparse.OptionGroup.add_option(self, *args,
688 remote_pass_through=True,
689 **kwargs)
690
691
Ryan Cuif7f24692012-05-18 16:35:33 -0700692# pylint: disable=W0613
Ryan Cui5ba7e152012-05-10 14:36:52 -0700693def check_path(option, opt, value):
694 """Expand paths and make them absolute."""
695 expanded = osutils.ExpandPath(value)
696 if expanded == '/':
697 raise optparse.OptionValueError('Invalid path %s specified for %s'
698 % (expanded, opt))
699
700 return expanded
701
Ryan Cui6196fc22012-06-27 17:52:18 -0700702# pylint: disable=W0613
703def check_gs_path(option, opt, value):
704 """Expand paths and make them absolute."""
705 value = value.strip().rstrip('/')
706 if not value.startswith('gs://'):
707 raise optparse.OptionValueError('Invalid gs path %s specified for %s'
708 % (value, opt))
709
710 return value
711
Ryan Cui5ba7e152012-05-10 14:36:52 -0700712
713class CustomOption(optparse.Option):
714 """Subclass Option class to implement pass-through and path evaluation."""
Ryan Cui6196fc22012-06-27 17:52:18 -0700715 TYPES = optparse.Option.TYPES + ('path', 'gs_path')
Ryan Cui5ba7e152012-05-10 14:36:52 -0700716 TYPE_CHECKER = optparse.Option.TYPE_CHECKER.copy()
717 TYPE_CHECKER['path'] = check_path
Ryan Cui6196fc22012-06-27 17:52:18 -0700718 TYPE_CHECKER['gs_path'] = check_gs_path
Ryan Cui5ba7e152012-05-10 14:36:52 -0700719
Ryan Cui79319ab2012-05-21 12:59:18 -0700720 ACTIONS = optparse.Option.ACTIONS + ('extend',)
721 STORE_ACTIONS = optparse.Option.STORE_ACTIONS + ('extend',)
722 TYPED_ACTIONS = optparse.Option.TYPED_ACTIONS + ('extend',)
723 ALWAYS_TYPED_ACTIONS = optparse.Option.ALWAYS_TYPED_ACTIONS + ('extend',)
724
Ryan Cui5ba7e152012-05-10 14:36:52 -0700725 def __init__(self, *args, **kwargs):
726 # The remote_pass_through argument specifies whether we should directly
727 # pass the argument (with its value) onto the remote trybot.
728 self.pass_through = kwargs.pop('remote_pass_through', False)
729 optparse.Option.__init__(self, *args, **kwargs)
730
731 def take_action(self, action, dest, opt, value, values, parser):
Ryan Cui79319ab2012-05-21 12:59:18 -0700732 if action == 'extend':
Mike Frysingerd6925b52012-07-16 16:11:00 -0400733 # If there is extra spaces between each argument, we get '' which later
734 # code barfs on, so skip those. e.g. We see this with the forms:
735 # cbuildbot -p 'proj:branch ' ...
736 # cbuildbot -p ' proj:branch' ...
737 # cbuildbot -p 'proj:branch proj2:branch' ...
738 lvalue = value.split()
Ryan Cui79319ab2012-05-21 12:59:18 -0700739 values.ensure_value(dest, []).extend(lvalue)
740 else:
741 optparse.Option.take_action(self, action, dest, opt, value, values,
742 parser)
743
Ryan Cui5ba7e152012-05-10 14:36:52 -0700744 if self.pass_through:
745 parser.values.pass_through_args.append(opt)
746 if self.nargs and self.nargs > 1:
747 # value is a tuple if nargs > 1
748 string_list = [str(val) for val in list(value)]
749 parser.values.pass_through_args.extend(string_list)
750 elif value:
751 parser.values.pass_through_args.append(str(value))
752
753
Brian Harring3fec5a82012-03-01 05:57:03 -0800754def _CreateParser():
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700755 """Generate and return the parser with all the options."""
Brian Harring3fec5a82012-03-01 05:57:03 -0800756 # Parse options
757 usage = "usage: %prog [options] buildbot_config"
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700758 parser = CustomParser(usage=usage, option_class=CustomOption)
Brian Harring3fec5a82012-03-01 05:57:03 -0800759
760 # Main options
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700761 # The remote_pass_through parameter to add_option is implemented by the
762 # CustomOption class. See CustomOption for more information.
Brian Harring3fec5a82012-03-01 05:57:03 -0800763 parser.add_option('-a', '--all', action='store_true', dest='print_all',
764 default=False,
765 help=('List all of the buildbot configs available. Use '
766 'with the --list option'))
Ryan Cuie1e4e662012-05-21 16:39:46 -0700767 parser.add_remote_option('-b', '--branch',
768 help='The manifest branch to test. The branch to '
769 'check the buildroot out to.')
Ryan Cui5ba7e152012-05-10 14:36:52 -0700770 parser.add_option('-r', '--buildroot', dest='buildroot', type='path',
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700771 help='Root directory where source is checked out to, and '
772 'where the build occurs. For external build configs, '
773 "defaults to 'trybot' directory at top level of your "
774 'repo-managed checkout.')
775 parser.add_remote_option('--chrome_rev', default=None, type='string',
776 action='callback', dest='chrome_rev',
777 callback=_CheckChromeRevOption,
778 help=('Revision of Chrome to use, of type [%s]'
779 % '|'.join(constants.VALID_CHROME_REVISIONS)))
Ryan Cui79319ab2012-05-21 12:59:18 -0700780 parser.add_remote_option('-g', '--gerrit-patches', action='extend',
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700781 default=[], type='string',
782 metavar="'Id1 *int_Id2...IdN'",
783 help=("Space-separated list of short-form Gerrit "
784 "Change-Id's or change numbers to patch. "
785 "Please prepend '*' to internal Change-Id's"))
Brian Harring3fec5a82012-03-01 05:57:03 -0800786 parser.add_option('-l', '--list', action='store_true', dest='list',
787 default=False,
788 help=('List the suggested trybot configs to use. Use '
789 '--all to list all of the available configs.'))
Ryan Cui54da0702012-04-19 18:38:08 -0700790 parser.add_option('--local', default=False, action='store_true',
791 help=('Specifies that this tryjob should be run locally.'))
Ryan Cui79319ab2012-05-21 12:59:18 -0700792 parser.add_option('-p', '--local-patches', action='extend', default=[],
Brian Harring3fec5a82012-03-01 05:57:03 -0800793 metavar="'<project1>[:<branch1>]...<projectN>[:<branchN>]'",
794 help=('Space-separated list of project branches with '
795 'patches to apply. Projects are specified by name. '
796 'If no branch is specified the current branch of the '
797 'project will be used.'))
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700798 parser.add_remote_option('--profile', default=None, type='string',
799 action='store', dest='profile',
800 help='Name of profile to sub-specify board variant.')
Brian Harring3fec5a82012-03-01 05:57:03 -0800801 parser.add_option('--remote', default=False, action='store_true',
Brian Harring3fec5a82012-03-01 05:57:03 -0800802 help=('Specifies that this tryjob should be run remotely.'))
Brian Harring219a2b82012-07-18 15:30:12 -0700803 parser.add_option('--remote-description', default=None,
804 help=('Attach an optional description to a --remote run '
805 'to make it easier to identify the results when it '
806 'finishes.'))
Brian Harring3fec5a82012-03-01 05:57:03 -0800807
Ryan Cuif4f84be2012-07-09 18:50:41 -0700808 #
Brian Harring3fec5a82012-03-01 05:57:03 -0800809 # Advanced options
Ryan Cuif4f84be2012-07-09 18:50:41 -0700810 #
811
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700812 group = CustomGroup(
Brian Harring3fec5a82012-03-01 05:57:03 -0800813 parser,
814 'Advanced Options',
815 'Caution: use these options at your own risk.')
816
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700817 group.add_remote_option('--buildbot', dest='buildbot', action='store_true',
818 default=False, help='This is running on a buildbot')
819 group.add_remote_option('--buildnumber', help='build number', type='int',
820 default=0)
Ryan Cui5ba7e152012-05-10 14:36:52 -0700821 group.add_option('--chrome_root', default=None, type='path',
822 action='callback', callback=_CheckChromeRootOption,
823 dest='chrome_root', help='Local checkout of Chrome to use.')
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700824 group.add_remote_option('--chrome_version', default=None, type='string',
825 action='callback', dest='chrome_version',
826 callback=_CheckChromeVersionOption,
827 help='Used with SPEC logic to force a particular SVN '
828 'revision of chrome rather than the latest.')
829 group.add_remote_option('--clobber', action='store_true', dest='clobber',
830 default=False,
831 help='Clears an old checkout before syncing')
Yu-Ju Hong52134292012-06-28 12:50:42 -0700832 group.add_remote_option('--hwtest', dest='hwtest', action='store_true',
833 default=False,
834 help='This adds HW test for remote trybot')
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700835 group.add_remote_option('--lkgm', action='store_true', dest='lkgm',
836 default=False,
837 help='Sync to last known good manifest blessed by '
838 'PFQ')
Ryan Cui5ba7e152012-05-10 14:36:52 -0700839 parser.add_option('--log_dir', dest='log_dir', type='path',
Brian Harring3fec5a82012-03-01 05:57:03 -0800840 help=('Directory where logs are stored.'))
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700841 group.add_remote_option('--maxarchives', dest='max_archive_builds',
842 default=3, type='int',
843 help="Change the local saved build count limit.")
Ryan Cuibbd3d4b2012-08-17 12:20:37 -0700844 parser.add_remote_option('--manifest-repo-url',
845 help=('Overrides the default manifest repo url.'))
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700846 group.add_remote_option('--noarchive', action='store_false', dest='archive',
847 default=True, help="Don't run archive stage.")
Ryan Cuif7f24692012-05-18 16:35:33 -0700848 group.add_remote_option('--nobootstrap', action='store_false',
849 dest='bootstrap', default=True,
850 help="Don't checkout and run from a standalone "
851 "chromite repo.")
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700852 group.add_remote_option('--nobuild', action='store_false', dest='build',
853 default=True,
854 help="Don't actually build (for cbuildbot dev)")
855 group.add_remote_option('--noclean', action='store_false', dest='clean',
856 default=True, help="Don't clean the buildroot")
Ryan Cuif7f24692012-05-18 16:35:33 -0700857 group.add_remote_option('--nocgroups', action='store_false', dest='cgroups',
858 default=True,
859 help='Disable cbuildbots usage of cgroups.')
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700860 group.add_remote_option('--noprebuilts', action='store_false',
861 dest='prebuilts', default=True,
862 help="Don't upload prebuilts.")
863 group.add_remote_option('--nosync', action='store_false', dest='sync',
864 default=True, help="Don't sync before building.")
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700865 group.add_remote_option('--notests', action='store_false', dest='tests',
866 default=True,
867 help='Override values from buildconfig and run no '
868 'tests.')
869 group.add_remote_option('--nouprev', action='store_false', dest='uprev',
870 default=True,
871 help='Override values from buildconfig and never '
872 'uprev.')
Brian Harring3fec5a82012-03-01 05:57:03 -0800873 group.add_option('--reference-repo', action='store', default=None,
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700874 dest='reference_repo',
875 help='Reuse git data stored in an existing repo '
876 'checkout. This can drastically reduce the network '
877 'time spent setting up the trybot checkout. By '
878 "default, if this option isn't given but cbuildbot "
879 'is invoked from a repo checkout, cbuildbot will '
880 'use the repo root.')
Ryan Cuicedd8a52012-03-22 02:28:35 -0700881 group.add_option('--resume', action='store_true', default=False,
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700882 help='Skip stages already successfully completed.')
883 group.add_remote_option('--timeout', action='store', type='int', default=0,
884 help='Specify the maximum amount of time this job '
885 'can run for, at which point the build will be '
886 'aborted. If set to zero, then there is no '
887 'timeout.')
Ryan Cui39bdbbf2012-02-29 16:15:39 -0800888 group.add_option('--test-tryjob', action='store_true',
889 default=False,
890 help='Submit a tryjob to the test repository. Will not '
891 'show up on the production trybot waterfall.')
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700892 group.add_remote_option('--validation_pool', default=None,
893 help='Path to a pickled validation pool. Intended '
894 'for use only with the commit queue.')
895 group.add_remote_option('--version', dest='force_version', default=None,
896 help='Used with manifest logic. Forces use of this '
897 'version rather than create or get latest.')
Brian Harringab8fb5c2012-07-18 11:28:22 -0700898 group.add_remote_option('--cq-gerrit-query', dest='cq_gerrit_override',
899 default=None,
900 help=
901 "If given, this gerrit query will be used to find what patches to test, "
902 "rather than the normal 'CommitReady=2 AND Verified=1 AND CodeReview=2' "
903 "query it defaults to. Use with care- note additionally this setting "
904 "only has an effect if the buildbot target is a cq target, and we're "
905 "in buildbot mode.")
Brian Harring3fec5a82012-03-01 05:57:03 -0800906
907 parser.add_option_group(group)
908
Ryan Cuif4f84be2012-07-09 18:50:41 -0700909 #
910 # Hidden options.
911 #
912
913 # The base GS URL (gs://<bucket_name>/<path>) to archive artifacts to.
914 parser.add_remote_option('--archive-base', type='gs_path',
915 help=optparse.SUPPRESS_HELP)
916 # bootstrap-args are not verified by the bootstrap code. It gets passed
917 # direcly to the bootstrap re-execution.
918 parser.add_remote_option('--bootstrap-args', action='append',
919 default=[], help=optparse.SUPPRESS_HELP)
Ryan Cuif37608e2012-07-10 14:28:48 -0700920 # Specify to use Gerrit Source for building Chrome. Implies
921 # --chrome_rev=CHROME_REV_TOT.
922 parser.add_option('--gerrit-chrome', action='callback', default=False,
923 callback=_CheckGerritChromeOption, dest='gerrit_chrome',
924 help=optparse.SUPPRESS_HELP)
Ryan Cuif4f84be2012-07-09 18:50:41 -0700925 parser.add_option('--pass-through', dest='pass_through_args', action='append',
926 type='string', default=[], help=optparse.SUPPRESS_HELP)
927 # Used for handling forwards/backwards compatibility for --resume and
928 # --bootstrap.
929 parser.add_option('--reexec-api-version', dest='output_api_version',
930 action='store_true', default=False,
931 help=optparse.SUPPRESS_HELP)
932 # Indicates this is running on a remote trybot machine.
933 parser.add_option('--remote-trybot', dest='remote_trybot',
934 action='store_true', default=False,
935 help=optparse.SUPPRESS_HELP)
936 # Patches uploaded by trybot client when run using the -p option.
937 parser.add_remote_option('--remote-patches', action='extend', default=[],
938 help=optparse.SUPPRESS_HELP)
939 # Specify specific remote tryslaves to run on.
940 parser.add_option('--slaves', action='extend', default=[],
941 help=optparse.SUPPRESS_HELP)
942 parser.add_option('--sourceroot', type='path', default=constants.SOURCE_ROOT,
943 help=optparse.SUPPRESS_HELP)
944 # Causes cbuildbot to bootstrap itself twice, in the sequence A->B->C.
945 # A(unpatched) patches and bootstraps B. B patches and bootstraps C.
946 parser.add_remote_option('--test-bootstrap', action='store_true',
947 default=False, help=optparse.SUPPRESS_HELP)
Brian Harringf611e6e2012-07-17 18:47:44 -0700948 # Note the default here needs to be hardcoded to 3; that is the last version
949 # that lacked this functionality.
950 # This is used so that cbuildbot when processing tryjobs from
951 # older chromite instances, we can use it for handling compatibility.
952 parser.add_option('--remote-version', default=3, type=int, action='store',
953 help=optparse.SUPPRESS_HELP)
Ryan Cuif4f84be2012-07-09 18:50:41 -0700954
955 #
Brian Harring3fec5a82012-03-01 05:57:03 -0800956 # Debug options
Ryan Cuif4f84be2012-07-09 18:50:41 -0700957 #
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700958 group = CustomGroup(parser, "Debug Options")
Brian Harring3fec5a82012-03-01 05:57:03 -0800959
Ryan Cuia25d8eb2012-07-11 14:54:27 -0700960 group.add_remote_option('--debug', action='store_true', default=None,
961 help='Override some options to run as a developer.')
Brian Harring3fec5a82012-03-01 05:57:03 -0800962 group.add_option('--dump_config', action='store_true', dest='dump_config',
963 default=False,
964 help='Dump out build config options, and exit.')
965 group.add_option('--notee', action='store_false', dest='tee', default=True,
966 help="Disable logging and internal tee process. Primarily "
967 "used for debugging cbuildbot itself.")
968 parser.add_option_group(group)
969 return parser
970
971
Ryan Cui85867972012-02-23 18:21:49 -0800972def _FinishParsing(options, args):
973 """Perform some parsing tasks that need to take place after optparse.
974
975 This function needs to be easily testable! Keep it free of
976 environment-dependent code. Put more detailed usage validation in
977 _PostParseCheck().
Brian Harring3fec5a82012-03-01 05:57:03 -0800978
979 Args:
Ryan Cui85867972012-02-23 18:21:49 -0800980 options, args: The options/args object returned by optparse
Brian Harring3fec5a82012-03-01 05:57:03 -0800981 """
Brian Harring07039b52012-05-13 17:56:47 -0700982 # Setup logging levels first so any parsing triggered log messages
983 # are appropriately filtered.
984 logging.getLogger().setLevel(
985 logging.DEBUG if options.debug else logging.INFO)
986
Brian Harring3fec5a82012-03-01 05:57:03 -0800987 if options.chrome_root:
988 if options.chrome_rev != constants.CHROME_REV_LOCAL:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700989 cros_build_lib.Die('Chrome rev must be %s if chrome_root is set.' %
990 constants.CHROME_REV_LOCAL)
Brian Harring3fec5a82012-03-01 05:57:03 -0800991 else:
992 if options.chrome_rev == constants.CHROME_REV_LOCAL:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700993 cros_build_lib.Die('Chrome root must be set if chrome_rev is %s.' %
994 constants.CHROME_REV_LOCAL)
Brian Harring3fec5a82012-03-01 05:57:03 -0800995
996 if options.chrome_version:
997 if options.chrome_rev != constants.CHROME_REV_SPEC:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700998 cros_build_lib.Die('Chrome rev must be %s if chrome_version is set.' %
999 constants.CHROME_REV_SPEC)
Brian Harring3fec5a82012-03-01 05:57:03 -08001000 else:
1001 if options.chrome_rev == constants.CHROME_REV_SPEC:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001002 cros_build_lib.Die(
1003 'Chrome rev must not be %s if chrome_version is not set.'
1004 % constants.CHROME_REV_SPEC)
Brian Harring3fec5a82012-03-01 05:57:03 -08001005
Ryan Cuif37608e2012-07-10 14:28:48 -07001006 if options.gerrit_chrome:
1007 if options.remote_trybot or options.remote:
1008 cros_build_lib.Die('Cannot use --gerrit-chrome with remote trybots!')
1009 elif options.chrome_rev != constants.CHROME_REV_TOT:
1010 cros_build_lib.Die('Chrome rev must be %s if chrome_root is set.' %
1011 constants.CHROME_REV_TOT)
1012
Ryan Cuieaa9efd2012-04-25 17:56:45 -07001013 patches = bool(options.gerrit_patches or options.local_patches)
1014 if options.remote:
1015 if options.local:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001016 cros_build_lib.Die('Cannot specify both --remote and --local')
Ryan Cui54da0702012-04-19 18:38:08 -07001017
Ryan Cuieaa9efd2012-04-25 17:56:45 -07001018 if not options.buildbot and not patches:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001019 cros_build_lib.Die('Must provide patches when running with --remote.')
Brian Harring3fec5a82012-03-01 05:57:03 -08001020
Ryan Cuieaa9efd2012-04-25 17:56:45 -07001021 # --debug needs to be explicitly passed through for remote invocations.
1022 release_mode_with_patches = (options.buildbot and patches and
1023 '--debug' not in options.pass_through_args)
1024 else:
1025 if len(args) > 1:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001026 cros_build_lib.Die('Multiple configs not supported if not running with '
Brian Harringf1aad832012-07-18 10:46:39 -07001027 '--remote. Got %r', args)
Ryan Cuieaa9efd2012-04-25 17:56:45 -07001028
Ryan Cui79319ab2012-05-21 12:59:18 -07001029 if options.slaves:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001030 cros_build_lib.Die('Cannot use --slaves if not running with --remote.')
Ryan Cui79319ab2012-05-21 12:59:18 -07001031
Ryan Cuieaa9efd2012-04-25 17:56:45 -07001032 release_mode_with_patches = (options.buildbot and patches and
1033 not options.debug)
1034
David James5734ea32012-08-15 20:23:49 -07001035 # When running in release mode, make sure we are running with checked-in code.
1036 # We want checked-in cbuildbot/scripts to prevent errors, and we want to build
1037 # a release image with checked-in code for CrOS packages.
1038 if release_mode_with_patches:
1039 cros_build_lib.Die(
1040 'Cannot provide patches when running with --buildbot!')
1041
Ryan Cuiba41ad32012-03-08 17:15:29 -08001042 if options.buildbot and options.remote_trybot:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001043 cros_build_lib.Die(
1044 '--buildbot and --remote-trybot cannot be used together.')
Ryan Cuiba41ad32012-03-08 17:15:29 -08001045
Ryan Cui85867972012-02-23 18:21:49 -08001046 # Record whether --debug was set explicitly vs. it was inferred.
1047 options.debug_forced = False
1048 if options.debug:
1049 options.debug_forced = True
1050 else:
Ryan Cui16ca5812012-03-08 20:34:27 -08001051 # We don't set debug by default for
1052 # 1. --buildbot invocations.
1053 # 2. --remote invocations, because it needs to push changes to the tryjob
1054 # repo.
1055 options.debug = not options.buildbot and not options.remote
Brian Harring3fec5a82012-03-01 05:57:03 -08001056
Brian Harring3fec5a82012-03-01 05:57:03 -08001057
Brian Harring1d7ba942012-04-24 06:37:18 -07001058# pylint: disable=W0613
Ryan Cui85867972012-02-23 18:21:49 -08001059def _PostParseCheck(options, args):
1060 """Perform some usage validation after we've parsed the arguments
Brian Harring3fec5a82012-03-01 05:57:03 -08001061
Ryan Cui85867972012-02-23 18:21:49 -08001062 Args:
1063 options/args: The options/args object returned by optparse
1064 """
Ryan Cuie1e4e662012-05-21 16:39:46 -07001065 if not options.branch:
Chris Sosa126103a2012-06-18 09:03:17 -07001066 options.branch = cros_build_lib.GetChromiteTrackingBranch()
Ryan Cuie1e4e662012-05-21 16:39:46 -07001067
Ryan Cui5ba7e152012-05-10 14:36:52 -07001068 if options.local_patches and not repository.IsARepoRoot(options.sourceroot):
1069 raise Exception('Could not find repo checkout at %s!'
1070 % options.sourceroot)
1071
Brian Harring609dc4e2012-05-07 02:17:44 -07001072 if options.local_patches:
Brian Harring1d7ba942012-04-24 06:37:18 -07001073 options.local_patches = _CheckLocalPatches(
Brian Harring609dc4e2012-05-07 02:17:44 -07001074 options.sourceroot, options.local_patches)
Brian Harring1d7ba942012-04-24 06:37:18 -07001075
1076 default = os.environ.get('CBUILDBOT_DEFAULT_MODE')
1077 if (default and not any([options.local, options.buildbot,
1078 options.remote, options.remote_trybot])):
Brian Harring1b8c4c82012-05-29 23:03:04 -07001079 cros_build_lib.Info("CBUILDBOT_DEFAULT_MODE=%s env var detected, using it."
1080 % default)
Brian Harring1d7ba942012-04-24 06:37:18 -07001081 default = default.lower()
1082 if default == 'local':
1083 options.local = True
1084 elif default == 'remote':
1085 options.remote = True
1086 elif default == 'buildbot':
1087 options.buildbot = True
1088 else:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001089 cros_build_lib.Die("CBUILDBOT_DEFAULT_MODE value %s isn't supported. "
1090 % default)
Ryan Cui85867972012-02-23 18:21:49 -08001091
1092
1093def _ParseCommandLine(parser, argv):
1094 """Completely parse the commandline arguments"""
Brian Harring3fec5a82012-03-01 05:57:03 -08001095 (options, args) = parser.parse_args(argv)
Brian Harring37e559b2012-05-22 20:47:32 -07001096
1097 if options.output_api_version:
1098 print _REEXEC_API_VERSION
1099 sys.exit(0)
1100
Ryan Cui54da0702012-04-19 18:38:08 -07001101 if options.list:
1102 _PrintValidConfigs(options.print_all)
1103 sys.exit(0)
1104
Ryan Cui8be16062012-04-24 12:05:26 -07001105 # Strip out null arguments.
1106 # TODO(rcui): Remove when buildbot is fixed
1107 args = [arg for arg in args if arg]
1108 if not args:
1109 parser.error('Invalid usage. Use -h to see usage. Use -l to list '
1110 'supported configs.')
1111
Ryan Cui85867972012-02-23 18:21:49 -08001112 _FinishParsing(options, args)
1113 return options, args
1114
1115
1116def main(argv):
1117 # Set umask to 022 so files created by buildbot are readable.
1118 os.umask(022)
1119
Brian Harring1b8c4c82012-05-29 23:03:04 -07001120 if cros_build_lib.IsInsideChroot():
1121 cros_build_lib.Die('Please run cbuildbot from outside the chroot.')
Ryan Cui85867972012-02-23 18:21:49 -08001122
1123 parser = _CreateParser()
1124 (options, args) = _ParseCommandLine(parser, argv)
Brian Harring3fec5a82012-03-01 05:57:03 -08001125
Brian Harring3fec5a82012-03-01 05:57:03 -08001126 _PostParseCheck(options, args)
1127
1128 if options.remote:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001129 cros_build_lib.logger.setLevel(logging.WARNING)
Ryan Cui16ca5812012-03-08 20:34:27 -08001130
Brian Harring3fec5a82012-03-01 05:57:03 -08001131 # Verify configs are valid.
1132 for bot in args:
1133 _GetConfig(bot)
1134
1135 # Verify gerrit patches are valid.
Ryan Cui16ca5812012-03-08 20:34:27 -08001136 print 'Verifying patches...'
Ryan Cuie1e4e662012-05-21 16:39:46 -07001137 patch_pool = AcquirePoolFromOptions(options)
Ryan Cui16d9e1f2012-05-11 10:50:18 -07001138
Ryan Cuieaa9efd2012-04-25 17:56:45 -07001139 # --debug need to be explicitly passed through for remote invocations.
1140 if options.buildbot and '--debug' not in options.pass_through_args:
1141 _ConfirmRemoteBuildbotRun()
1142
Ryan Cui16ca5812012-03-08 20:34:27 -08001143 print 'Submitting tryjob...'
Ryan Cui16d9e1f2012-05-11 10:50:18 -07001144 tryjob = remote_try.RemoteTryJob(options, args, patch_pool.local_patches)
Ryan Cuia25d8eb2012-07-11 14:54:27 -07001145 tryjob.Submit(testjob=options.test_tryjob, dryrun=False)
Ryan Cui16ca5812012-03-08 20:34:27 -08001146 print 'Tryjob submitted!'
1147 print ('Go to %s to view the status of your job.'
Ryan Cui4906e1c2012-04-03 20:09:34 -07001148 % tryjob.GetTrybotWaterfallLink())
Brian Harring3fec5a82012-03-01 05:57:03 -08001149 sys.exit(0)
Ryan Cui54da0702012-04-19 18:38:08 -07001150 elif (not options.buildbot and not options.remote_trybot
1151 and not options.resume and not options.local):
Brian Harring1b8c4c82012-05-29 23:03:04 -07001152 cros_build_lib.Warning(
1153 'Running in LOCAL TRYBOT mode! Use --remote to submit REMOTE '
1154 'tryjobs. Use --local to suppress this message.')
1155 cros_build_lib.Warning(
Ryan Cui51591352012-07-09 15:15:53 -07001156 'In the future, --local will be required to run the local '
Brian Harring1b8c4c82012-05-29 23:03:04 -07001157 'trybot.')
Ryan Cui54da0702012-04-19 18:38:08 -07001158 time.sleep(5)
Brian Harring3fec5a82012-03-01 05:57:03 -08001159
Ryan Cui8be16062012-04-24 12:05:26 -07001160 # Only expecting one config
1161 bot_id = args[-1]
1162 build_config = _GetConfig(bot_id)
Brian Harring3fec5a82012-03-01 05:57:03 -08001163
1164 if options.reference_repo is None:
Ryan Cui5ba7e152012-05-10 14:36:52 -07001165 repo_path = os.path.join(options.sourceroot, '.repo')
Brian Harring3fec5a82012-03-01 05:57:03 -08001166 # If we're being run from a repo checkout, reuse the repo's git pool to
1167 # cut down on sync time.
1168 if os.path.exists(repo_path):
Ryan Cui5ba7e152012-05-10 14:36:52 -07001169 options.reference_repo = options.sourceroot
Brian Harring3fec5a82012-03-01 05:57:03 -08001170 elif options.reference_repo:
1171 if not os.path.exists(options.reference_repo):
1172 parser.error('Reference path %s does not exist'
1173 % (options.reference_repo,))
1174 elif not os.path.exists(os.path.join(options.reference_repo, '.repo')):
1175 parser.error('Reference path %s does not look to be the base of a '
1176 'repo checkout; no .repo exists in the root.'
1177 % (options.reference_repo,))
Ryan Cuid4a24212012-04-04 18:08:12 -07001178
Brian Harringf11bf682012-05-14 15:53:43 -07001179 if (options.buildbot or options.remote_trybot) and not options.resume:
Brian Harring470f6112012-03-02 11:47:10 -08001180 if not options.cgroups:
Ryan Cuid4a24212012-04-04 18:08:12 -07001181 parser.error('Options --buildbot/--remote-trybot and --nocgroups cannot '
1182 'be used together. Cgroup support is required for '
1183 'buildbot/remote-trybot mode.')
Brian Harring470f6112012-03-02 11:47:10 -08001184 if not cgroups.Cgroup.CgroupsSupported():
Ryan Cuid4a24212012-04-04 18:08:12 -07001185 parser.error('Option --buildbot/--remote-trybot was given, but this '
1186 'system does not support cgroups. Failing.')
Brian Harring3fec5a82012-03-01 05:57:03 -08001187
Brian Harring351ce442012-03-09 16:38:14 -08001188 missing = []
1189 for program in _BUILDBOT_REQUIRED_BINARIES:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001190 ret = cros_build_lib.RunCommand(
1191 'which %s' % program, shell=True, redirect_stderr=True,
1192 redirect_stdout=True, error_code_ok=True, print_cmd=False)
Brian Harring351ce442012-03-09 16:38:14 -08001193 if ret.returncode != 0:
1194 missing.append(program)
1195
1196 if missing:
Ryan Cuid4a24212012-04-04 18:08:12 -07001197 parser.error("Option --buildbot/--remote-trybot requires the following "
1198 "binaries which couldn't be found in $PATH: %s"
Brian Harring351ce442012-03-09 16:38:14 -08001199 % (', '.join(missing)))
1200
Brian Harring3fec5a82012-03-01 05:57:03 -08001201 if options.reference_repo:
1202 options.reference_repo = os.path.abspath(options.reference_repo)
1203
1204 if options.dump_config:
1205 # This works, but option ordering is bad...
1206 print 'Configuration %s:' % bot_id
1207 pretty_printer = pprint.PrettyPrinter(indent=2)
1208 pretty_printer.pprint(build_config)
1209 sys.exit(0)
1210
1211 if not options.buildroot:
1212 if options.buildbot:
1213 parser.error('Please specify a buildroot with the --buildroot option.')
Matt Tennantd55b1f42012-04-13 14:15:01 -07001214
Ryan Cui5ba7e152012-05-10 14:36:52 -07001215 options.buildroot = _DetermineDefaultBuildRoot(options.sourceroot,
1216 build_config['internal'])
Brian Harring470f6112012-03-02 11:47:10 -08001217 # We use a marker file in the buildroot to indicate the user has
1218 # consented to using this directory.
1219 if not os.path.exists(repository.GetTrybotMarkerPath(options.buildroot)):
1220 _ConfirmBuildRoot(options.buildroot)
Brian Harring3fec5a82012-03-01 05:57:03 -08001221
1222 # Sanity check of buildroot- specifically that it's not pointing into the
1223 # midst of an existing repo since git-repo doesn't support nesting.
Brian Harring3fec5a82012-03-01 05:57:03 -08001224 if (not repository.IsARepoRoot(options.buildroot) and
David James6b80dc62012-02-29 15:34:40 -08001225 repository.InARepoRepository(options.buildroot)):
Brian Harring3fec5a82012-03-01 05:57:03 -08001226 parser.error('Configured buildroot %s points into a repository checkout, '
1227 'rather than the root of it. This is not supported.'
1228 % options.buildroot)
1229
Brian Harringd166aaf2012-05-14 18:31:53 -07001230 log_file = None
1231 if options.tee:
1232 default_dir = os.path.join(options.buildroot, _DEFAULT_LOG_DIR)
1233 dirname = options.log_dir or default_dir
1234 log_file = os.path.join(dirname, _BUILDBOT_LOG_FILE)
1235
1236 osutils.SafeMakedirs(dirname)
1237 _BackupPreviousLog(log_file)
1238
Brian Harring1b8c4c82012-05-29 23:03:04 -07001239 with cros_build_lib.ContextManagerStack() as stack:
Brian Harringc2d09d92012-05-13 22:03:15 -07001240 critical_section = stack.Add(cleanup.EnforcedCleanupSection)
1241 stack.Add(sudo.SudoKeepAlive)
Brian Harringd166aaf2012-05-14 18:31:53 -07001242
Brian Harringc2d09d92012-05-13 22:03:15 -07001243 if not options.resume:
Brian Harring2bf55e12012-05-13 21:31:55 -07001244 # If we're in resume mode, use our parents tempdir rather than
1245 # nesting another layer.
Brian Harringc2d09d92012-05-13 22:03:15 -07001246 stack.Add(osutils.TempDirContextManager, 'cbuildbot-tmp')
1247 logging.debug("Cbuildbot tempdir is %r.", os.environ.get('TMP'))
Brian Harringd166aaf2012-05-14 18:31:53 -07001248
Brian Harringead69102012-07-31 15:54:07 -07001249 # TODO(ferringb): update this once https://gerrit.chromium.org/gerrit/25359
1250 # is landed- it's sensitive to the manifest-versions cache path.
1251 options.preserve_paths = set(['manifest-versions',
1252 'manifest-versions-internal'])
Brian Harringd166aaf2012-05-14 18:31:53 -07001253 if log_file is not None:
1254 stack.Add(tee.Tee, log_file)
Brian Harring2d8f9ff2012-06-30 15:58:28 -07001255 options.preserve_paths.add(_DEFAULT_LOG_DIR)
Brian Harringd166aaf2012-05-14 18:31:53 -07001256
Brian Harringc2d09d92012-05-13 22:03:15 -07001257 if options.cgroups:
1258 stack.Add(cgroups.SimpleContainChildren, 'cbuildbot')
Brian Harringa184efa2012-03-04 11:51:25 -08001259
Brian Harringc2d09d92012-05-13 22:03:15 -07001260 # Mark everything between EnforcedCleanupSection and here as having to
1261 # be rolled back via the contextmanager cleanup handlers. This
1262 # ensures that sudo bits cannot outlive cbuildbot, that anything
1263 # cgroups would kill gets killed, etc.
1264 critical_section.ForkWatchdog()
Brian Harringd166aaf2012-05-14 18:31:53 -07001265
Brian Harringc2d09d92012-05-13 22:03:15 -07001266 if options.timeout > 0:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001267 stack.Add(cros_build_lib.Timeout, options.timeout)
Brian Harringa184efa2012-03-04 11:51:25 -08001268
Brian Harringc2d09d92012-05-13 22:03:15 -07001269 if not options.buildbot:
1270 build_config = cbuildbot_config.OverrideConfigForTrybot(
1271 build_config,
1272 options.remote_trybot)
1273
1274 _RunBuildStagesWrapper(options, build_config)