blob: 4985702237b550766b882366be2ae2429d2a5bff [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
Brian Harring3fec5a82012-03-01 05:57:03 -080024from 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
Brian Harring3fec5a82012-03-01 05:57:03 -080028from chromite.buildbot import remote_try
29from chromite.buildbot import repository
30from chromite.buildbot import tee
Ryan Cui16d9e1f2012-05-11 10:50:18 -070031from chromite.buildbot import trybot_patch_pool
Brian Harring3fec5a82012-03-01 05:57:03 -080032
Brian Harringc92a7012012-02-29 10:11:34 -080033from chromite.lib import cgroups
Brian Harringa184efa2012-03-04 11:51:25 -080034from chromite.lib import cleanup
Brian Harringb6cf9142012-09-01 20:43:17 -070035from chromite.lib import commandline
Brian Harring1b8c4c82012-05-29 23:03:04 -070036from chromite.lib import cros_build_lib
Brian Harring511055e2012-10-10 02:58:59 -070037from chromite.lib import gerrit
David James97d95872012-11-16 15:09:56 -080038from chromite.lib import git
Brian Harringaf019fb2012-05-10 15:06:13 -070039from chromite.lib import osutils
Brian Harring511055e2012-10-10 02:58:59 -070040from chromite.lib import patch as cros_patch
David James6450a0a2012-12-04 07:59:53 -080041from chromite.lib import parallel
Brian Harring3fec5a82012-03-01 05:57:03 -080042from chromite.lib import sudo
43
Ryan Cuiadd49122012-03-21 22:19:58 -070044
Brian Harring1b8c4c82012-05-29 23:03:04 -070045cros_build_lib.STRICT_SUDO = True
Brian Harring3fec5a82012-03-01 05:57:03 -080046
47_DEFAULT_LOG_DIR = 'cbuildbot_logs'
48_BUILDBOT_LOG_FILE = 'cbuildbot.log'
49_DEFAULT_EXT_BUILDROOT = 'trybot'
50_DEFAULT_INT_BUILDROOT = 'trybot-internal'
Brian Harring3fec5a82012-03-01 05:57:03 -080051_DISTRIBUTED_TYPES = [constants.COMMIT_QUEUE_TYPE, constants.PFQ_TYPE,
52 constants.CANARY_TYPE, constants.CHROME_PFQ_TYPE,
53 constants.PALADIN_TYPE]
Brian Harring351ce442012-03-09 16:38:14 -080054_BUILDBOT_REQUIRED_BINARIES = ('pbzip2',)
Ryan Cui1c13a252012-10-16 15:00:16 -070055_API_VERSION_ATTR = 'api_version'
Brian Harring3fec5a82012-03-01 05:57:03 -080056
57
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070058def _PrintValidConfigs(display_all=False):
Brian Harring3fec5a82012-03-01 05:57:03 -080059 """Print a list of valid buildbot configs.
60
61 Arguments:
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070062 display_all: Print all configs. Otherwise, prints only configs with
63 trybot_list=True.
Brian Harring3fec5a82012-03-01 05:57:03 -080064 """
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070065 def _GetSortKey(config_name):
66 config_dict = cbuildbot_config.config[config_name]
67 return (not config_dict['trybot_list'], config_dict['description'],
68 config_name)
69
Brian Harring3fec5a82012-03-01 05:57:03 -080070 COLUMN_WIDTH = 45
71 print 'config'.ljust(COLUMN_WIDTH), 'description'
72 print '------'.ljust(COLUMN_WIDTH), '-----------'
73 config_names = cbuildbot_config.config.keys()
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070074 config_names.sort(key=_GetSortKey)
Brian Harring3fec5a82012-03-01 05:57:03 -080075 for name in config_names:
Ryan Cui4f6cf7e2012-04-18 16:12:27 -070076 if display_all or cbuildbot_config.config[name]['trybot_list']:
77 desc = cbuildbot_config.config[name].get('description')
78 desc = desc if desc else ''
Brian Harring3fec5a82012-03-01 05:57:03 -080079 print name.ljust(COLUMN_WIDTH), desc
80
81
82def _GetConfig(config_name):
83 """Gets the configuration for the build"""
84 if not cbuildbot_config.config.has_key(config_name):
85 print 'Non-existent configuration %s specified.' % config_name
86 print 'Please specify one of:'
87 _PrintValidConfigs()
88 sys.exit(1)
89
90 result = cbuildbot_config.config[config_name]
91
92 return result
93
94
Ryan Cuie1e4e662012-05-21 16:39:46 -070095def AcquirePoolFromOptions(options):
Ryan Cui16d9e1f2012-05-11 10:50:18 -070096 """Generate patch objects from passed in options.
Brian Harring3fec5a82012-03-01 05:57:03 -080097
98 Args:
Ryan Cui16d9e1f2012-05-11 10:50:18 -070099 options: The options object generated by optparse.
Brian Harring3fec5a82012-03-01 05:57:03 -0800100
Ryan Cuif7f24692012-05-18 16:35:33 -0700101 Returns:
102 trybot_patch_pool.TrybotPatchPool object.
103
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700104 Raises:
Brian Harring511055e2012-10-10 02:58:59 -0700105 gerrit.GerritException, cros_patch.PatchException
Brian Harring3fec5a82012-03-01 05:57:03 -0800106 """
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700107 gerrit_patches = []
108 local_patches = []
109 remote_patches = []
Brian Harring3fec5a82012-03-01 05:57:03 -0800110
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700111 if options.gerrit_patches:
Brian Harring511055e2012-10-10 02:58:59 -0700112 gerrit_patches = gerrit.GetGerritPatchInfo(
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700113 options.gerrit_patches)
114 for patch in gerrit_patches:
115 if patch.IsAlreadyMerged():
Brian Harring1b8c4c82012-05-29 23:03:04 -0700116 cros_build_lib.Warning('Patch %s has already been merged.' % str(patch))
Brian Harring3fec5a82012-03-01 05:57:03 -0800117
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700118 if options.local_patches:
David James97d95872012-11-16 15:09:56 -0800119 manifest = git.ManifestCheckout.Cached(options.sourceroot)
Brian Harring609dc4e2012-05-07 02:17:44 -0700120 local_patches = cros_patch.PrepareLocalPatches(manifest,
121 options.local_patches)
Brian Harring3fec5a82012-03-01 05:57:03 -0800122
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700123 if options.remote_patches:
124 remote_patches = cros_patch.PrepareRemotePatches(
125 options.remote_patches)
Brian Harring3fec5a82012-03-01 05:57:03 -0800126
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700127 return trybot_patch_pool.TrybotPatchPool(gerrit_patches, local_patches,
128 remote_patches)
Brian Harring3fec5a82012-03-01 05:57:03 -0800129
130
Brian Harring3fec5a82012-03-01 05:57:03 -0800131class Builder(object):
132 """Parent class for all builder types.
133
134 This class functions as a parent class for various build types. It's intended
135 use is builder_instance.Run().
136
137 Vars:
Brian Harring3fec5a82012-03-01 05:57:03 -0800138 build_config: The configuration dictionary from cbuildbot_config.
139 options: The options provided from optparse in main().
Ryan Cui5616a512012-08-17 13:39:36 -0700140 archive_urls: Where our artifacts for this builder will be archived.
Brian Harring3fec5a82012-03-01 05:57:03 -0800141 release_tag: The associated "chrome os version" of this build.
Brian Harring3fec5a82012-03-01 05:57:03 -0800142 """
143
Ryan Cuie1e4e662012-05-21 16:39:46 -0700144 def __init__(self, options, build_config):
Brian Harring3fec5a82012-03-01 05:57:03 -0800145 """Initializes instance variables. Must be called by all subclasses."""
Brian Harring3fec5a82012-03-01 05:57:03 -0800146 self.build_config = build_config
147 self.options = options
148
149 # TODO, Remove here and in config after bug chromium-os:14649 is fixed.
150 if self.build_config['chromeos_official']:
151 os.environ['CHROMEOS_OFFICIAL'] = '1'
152
David James58e0c092012-03-04 20:31:12 -0800153 self.archive_stages = {}
Brian Harring3fec5a82012-03-01 05:57:03 -0800154 self.archive_urls = {}
155 self.release_tag = None
Brian Harring76d1bf62012-06-01 13:52:48 -0700156 self.patch_pool = trybot_patch_pool.TrybotPatchPool()
Brian Harring3fec5a82012-03-01 05:57:03 -0800157
Ryan Cuie1e4e662012-05-21 16:39:46 -0700158 bs.BuilderStage.SetManifestBranch(self.options.branch)
Ryan Cuif7f24692012-05-18 16:35:33 -0700159
Brian Harring3fec5a82012-03-01 05:57:03 -0800160 def Initialize(self):
161 """Runs through the initialization steps of an actual build."""
Ryan Cuif7f24692012-05-18 16:35:33 -0700162 if self.options.resume:
163 results_lib.LoadCheckpoint(self.options.buildroot)
Brian Harring3fec5a82012-03-01 05:57:03 -0800164
Brian Harring3fec5a82012-03-01 05:57:03 -0800165 self._RunStage(stages.CleanUpStage)
166
167 def _GetStageInstance(self, stage, *args, **kwargs):
168 """Helper function to get an instance given the args.
169
David James944a48e2012-03-07 12:19:03 -0800170 Useful as almost all stages just take in options and build_config.
Brian Harring3fec5a82012-03-01 05:57:03 -0800171 """
David James944a48e2012-03-07 12:19:03 -0800172 config = kwargs.pop('config', self.build_config)
173 return stage(self.options, config, *args, **kwargs)
Brian Harring3fec5a82012-03-01 05:57:03 -0800174
175 def _SetReleaseTag(self):
176 """Sets the release tag from the manifest_manager.
177
178 Must be run after sync stage as syncing enables us to have a release tag.
179 """
180 # Extract version we have decided to build into self.release_tag.
181 manifest_manager = stages.ManifestVersionedSyncStage.manifest_manager
182 if manifest_manager:
183 self.release_tag = manifest_manager.current_version
184
185 def _RunStage(self, stage, *args, **kwargs):
186 """Wrapper to run a stage."""
187 stage_instance = self._GetStageInstance(stage, *args, **kwargs)
188 return stage_instance.Run()
189
190 def GetSyncInstance(self):
191 """Returns an instance of a SyncStage that should be run.
192
193 Subclasses must override this method.
194 """
195 raise NotImplementedError()
196
197 def RunStages(self):
198 """Subclasses must override this method. Runs the appropriate code."""
199 raise NotImplementedError()
200
Brian Harring3fec5a82012-03-01 05:57:03 -0800201 def _ShouldReExecuteInBuildRoot(self):
202 """Returns True if this build should be re-executed in the buildroot."""
203 abs_buildroot = os.path.abspath(self.options.buildroot)
204 return not os.path.abspath(__file__).startswith(abs_buildroot)
205
206 def _ReExecuteInBuildroot(self, sync_instance):
207 """Reexecutes self in buildroot and returns True if build succeeds.
208
209 This allows the buildbot code to test itself when changes are patched for
210 buildbot-related code. This is a no-op if the buildroot == buildroot
211 of the running chromite checkout.
212
213 Args:
214 sync_instance: Instance of the sync stage that was run to sync.
215
216 Returns:
217 True if the Build succeeded.
218 """
Brian Harring3fec5a82012-03-01 05:57:03 -0800219 if not self.options.resume:
Ryan Cuif7f24692012-05-18 16:35:33 -0700220 results_lib.WriteCheckpoint(self.options.buildroot)
Brian Harring3fec5a82012-03-01 05:57:03 -0800221
Ryan Cui1c13a252012-10-16 15:00:16 -0700222 args = stages.BootstrapStage.FilterArgsForTargetCbuildbot(
223 self.options.buildroot, constants.PATH_TO_CBUILDBOT, self.options)
Brian Harring37e559b2012-05-22 20:47:32 -0700224
Brian Harring3fec5a82012-03-01 05:57:03 -0800225 # Re-write paths to use absolute paths.
226 # Suppress any timeout options given from the commandline in the
227 # invoked cbuildbot; our timeout will enforce it instead.
Ryan Cui1c13a252012-10-16 15:00:16 -0700228 args += ['--resume', '--timeout', '0', '--notee', '--nocgroups',
229 '--buildroot', os.path.abspath(self.options.buildroot)]
Brian Harringae0a5322012-09-15 01:46:51 -0700230
Brian Harring3fec5a82012-03-01 05:57:03 -0800231 if self.options.chrome_root:
Ryan Cui1c13a252012-10-16 15:00:16 -0700232 args += ['--chrome_root',
233 os.path.abspath(self.options.chrome_root)]
Brian Harring3fec5a82012-03-01 05:57:03 -0800234
235 if stages.ManifestVersionedSyncStage.manifest_manager:
236 ver = stages.ManifestVersionedSyncStage.manifest_manager.current_version
Ryan Cui1c13a252012-10-16 15:00:16 -0700237 args += ['--version', ver]
Brian Harring3fec5a82012-03-01 05:57:03 -0800238
239 if isinstance(sync_instance, stages.CommitQueueSyncStage):
240 vp_file = sync_instance.SaveValidationPool()
Ryan Cui1c13a252012-10-16 15:00:16 -0700241 args += ['--validation_pool', vp_file]
Brian Harring3fec5a82012-03-01 05:57:03 -0800242
243 # Re-run the command in the buildroot.
244 # Finally, be generous and give the invoked cbuildbot 30s to shutdown
245 # when something occurs. It should exit quicker, but the sigterm may
246 # hit while the system is particularly busy.
Brian Harring1b8c4c82012-05-29 23:03:04 -0700247 return_obj = cros_build_lib.RunCommand(
Ryan Cui1c13a252012-10-16 15:00:16 -0700248 args, cwd=self.options.buildroot, error_code_ok=True, kill_timeout=30)
Brian Harring3fec5a82012-03-01 05:57:03 -0800249 return return_obj.returncode == 0
250
Ryan Cuif7f24692012-05-18 16:35:33 -0700251 def _InitializeTrybotPatchPool(self):
252 """Generate patch pool from patches specified on the command line.
253
254 Do this only if we need to patch changes later on.
255 """
256 changes_stage = stages.PatchChangesStage.StageNamePrefix()
257 check_func = results_lib.Results.PreviouslyCompletedRecord
258 if not check_func(changes_stage) or self.options.bootstrap:
Ryan Cuie1e4e662012-05-21 16:39:46 -0700259 self.patch_pool = AcquirePoolFromOptions(self.options)
Ryan Cuif7f24692012-05-18 16:35:33 -0700260
261 def _GetBootstrapStage(self):
262 """Constructs and returns the BootStrapStage object.
263
264 We return None when there are no chromite patches to test, and
265 --test-bootstrap wasn't passed in.
266 """
267 stage = None
268 chromite_pool = self.patch_pool.Filter(project=constants.CHROMITE_PROJECT)
Ryan Cui5616a512012-08-17 13:39:36 -0700269 manifest_pool = self.patch_pool.FilterManifest()
David James97d95872012-11-16 15:09:56 -0800270 chromite_branch = git.GetChromiteTrackingBranch()
Ryan Cui5616a512012-08-17 13:39:36 -0700271 if (chromite_pool or manifest_pool or self.options.test_bootstrap
Ryan Cuie1e4e662012-05-21 16:39:46 -0700272 or chromite_branch != self.options.branch):
Ryan Cuif7f24692012-05-18 16:35:33 -0700273 stage = stages.BootstrapStage(self.options, self.build_config,
Ryan Cui5616a512012-08-17 13:39:36 -0700274 chromite_pool, manifest_pool)
Ryan Cuif7f24692012-05-18 16:35:33 -0700275 return stage
276
Brian Harring3fec5a82012-03-01 05:57:03 -0800277 def Run(self):
Ryan Cuif7f24692012-05-18 16:35:33 -0700278 """Main runner for this builder class. Runs build and prints summary.
279
280 Returns:
281 Whether the build succeeded.
282 """
283 self._InitializeTrybotPatchPool()
284
285 if self.options.bootstrap:
286 bootstrap_stage = self._GetBootstrapStage()
287 if bootstrap_stage:
288 # BootstrapStage blocks on re-execution of cbuildbot.
289 bootstrap_stage.Run()
290 return bootstrap_stage.returncode == 0
291
Brian Harring3fec5a82012-03-01 05:57:03 -0800292 print_report = True
David James3d4d3502012-04-09 15:12:06 -0700293 exception_thrown = False
Brian Harring3fec5a82012-03-01 05:57:03 -0800294 success = True
295 try:
296 self.Initialize()
297 sync_instance = self.GetSyncInstance()
298 sync_instance.Run()
299 self._SetReleaseTag()
300
Ryan Cui967f7cc2012-08-17 13:01:12 -0700301 # Filter out patches to manifest, since PatchChangesStage can't handle
302 # them. Manifest patches are patched in the BootstrapStage.
303 non_manifest_patches = self.patch_pool.FilterManifest(negate=True)
304 if non_manifest_patches:
305 self._RunStage(stages.PatchChangesStage, non_manifest_patches)
Brian Harring3fec5a82012-03-01 05:57:03 -0800306
307 if self._ShouldReExecuteInBuildRoot():
308 print_report = False
309 success = self._ReExecuteInBuildroot(sync_instance)
310 else:
311 self.RunStages()
David James7fbf2d42012-07-14 18:23:49 -0700312 except results_lib.StepFailure:
313 # StepFailure exceptions are already recorded in the report, so there
314 # is no need to print these tracebacks twice.
315 exception_thrown = True
316 if not print_report:
317 raise
David James3d4d3502012-04-09 15:12:06 -0700318 except Exception:
319 exception_thrown = True
320 raise
Brian Harring3fec5a82012-03-01 05:57:03 -0800321 finally:
322 if print_report:
Ryan Cuif7f24692012-05-18 16:35:33 -0700323 results_lib.WriteCheckpoint(self.options.buildroot)
Brian Harring3fec5a82012-03-01 05:57:03 -0800324 print '\n\n\n@@@BUILD_STEP Report@@@\n'
325 results_lib.Results.Report(sys.stdout, self.archive_urls,
326 self.release_tag)
327 success = results_lib.Results.BuildSucceededSoFar()
David James3d4d3502012-04-09 15:12:06 -0700328 if exception_thrown and success:
329 success = False
David Jamesbb20ac82012-07-18 10:59:16 -0700330 cros_build_lib.PrintBuildbotStepWarnings()
331 print """\
David James3d4d3502012-04-09 15:12:06 -0700332Exception thrown, but all stages marked successful. This is an internal error,
333because the stage that threw the exception should be marked as failing."""
Brian Harring3fec5a82012-03-01 05:57:03 -0800334
335 return success
336
337
338class SimpleBuilder(Builder):
339 """Builder that performs basic vetting operations."""
340
341 def GetSyncInstance(self):
342 """Sync to lkgm or TOT as necessary.
343
344 Returns: the instance of the sync stage that was run.
345 """
Chris Sosa52a81b62012-11-14 06:12:54 -0800346 if self.build_config['use_lkgm']:
Brian Harring3fec5a82012-03-01 05:57:03 -0800347 sync_stage = self._GetStageInstance(stages.LKGMSyncStage)
Chris Sosa52a81b62012-11-14 06:12:54 -0800348 elif self.build_config['use_chrome_lkgm']:
349 sync_stage = self._GetStageInstance(stages.ChromeLKGMSyncStage)
Brian Harring3fec5a82012-03-01 05:57:03 -0800350 else:
351 sync_stage = self._GetStageInstance(stages.SyncStage)
352
353 return sync_stage
354
David James58e0c092012-03-04 20:31:12 -0800355 def _RunBackgroundStagesForBoard(self, board):
356 """Run background board-specific stages for the specified board."""
David James58e0c092012-03-04 20:31:12 -0800357 archive_stage = self.archive_stages[board]
David James944a48e2012-03-07 12:19:03 -0800358 configs = self.build_config['board_specific_configs']
359 config = configs.get(board, self.build_config)
360 stage_list = [[stages.VMTestStage, board, archive_stage],
361 [stages.ChromeTestStage, board, archive_stage],
Mike Frysinger5e20ec42012-09-28 23:39:56 -0400362 [stages.SignerTestStage, board, archive_stage],
David James944a48e2012-03-07 12:19:03 -0800363 [stages.UnitTestStage, board],
Chris Sosa6ed77e52012-10-22 12:57:31 -0700364 [stages.UploadPrebuiltsStage, board, archive_stage],
365 [stages.DevInstallerPrebuiltsStage, board, archive_stage]]
Brian Harring3fec5a82012-03-01 05:57:03 -0800366
David James58e0c092012-03-04 20:31:12 -0800367 # We can not run hw tests without archiving the payloads.
368 if self.options.archive:
David James944a48e2012-03-07 12:19:03 -0800369 for suite in config['hw_tests']:
Peter Mayo85e94372012-08-17 12:42:14 -0400370 stage_list.append([stages.HWTestStage, board, archive_stage, suite])
Chris Sosab50dc932012-03-01 14:00:58 -0800371
Chris Sosa817b1f92012-07-19 15:00:23 -0700372 for suite in config['async_hw_tests']:
373 stage_list.append([stages.ASyncHWTestStage, board, archive_stage,
374 suite])
375
David James944a48e2012-03-07 12:19:03 -0800376 steps = [self._GetStageInstance(*x, config=config).Run for x in stage_list]
David James6450a0a2012-12-04 07:59:53 -0800377 parallel.RunParallelSteps(steps + [archive_stage.Run])
Brian Harring3fec5a82012-03-01 05:57:03 -0800378
379 def RunStages(self):
380 """Runs through build process."""
Brian Harring3fec5a82012-03-01 05:57:03 -0800381 # TODO(sosa): Split these out into classes.
Brian Harring3fec5a82012-03-01 05:57:03 -0800382 if self.build_config['build_type'] == constants.CHROOT_BUILDER_TYPE:
Mike Frysingerfddaeb52012-11-20 11:17:31 -0500383 self._RunStage(stages.UprevStage, boards=[], enter_chroot=False)
Mike Frysinger0fe33322012-10-31 22:02:49 -0400384 self._RunStage(stages.BuildBoardStage, [constants.CHROOT_BUILDER_BOARD])
Zdenek Behan62a57792012-08-31 15:09:08 +0200385 self._RunStage(stages.SDKPackageStage)
Brian Harring3fec5a82012-03-01 05:57:03 -0800386 self._RunStage(stages.SDKTestStage)
387 self._RunStage(stages.UploadPrebuiltsStage,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700388 constants.CHROOT_BUILDER_BOARD, None)
Brian Harring3fec5a82012-03-01 05:57:03 -0800389 elif self.build_config['build_type'] == constants.REFRESH_PACKAGES_TYPE:
Mike Frysinger0fe33322012-10-31 22:02:49 -0400390 self._RunStage(stages.BuildBoardStage)
Brian Harring3fec5a82012-03-01 05:57:03 -0800391 self._RunStage(stages.RefreshPackageStatusStage)
392 else:
Mike Frysinger0fe33322012-10-31 22:02:49 -0400393 self._RunStage(stages.BuildBoardStage)
Brian Harring3fec5a82012-03-01 05:57:03 -0800394 self._RunStage(stages.UprevStage)
Brian Harring3fec5a82012-03-01 05:57:03 -0800395
David James944a48e2012-03-07 12:19:03 -0800396 configs = self.build_config['board_specific_configs']
David James58e0c092012-03-04 20:31:12 -0800397 for board in self.build_config['boards']:
David James944a48e2012-03-07 12:19:03 -0800398 config = configs.get(board, self.build_config)
399 archive_stage = self._GetStageInstance(stages.ArchiveStage, board,
400 config=config)
David James58e0c092012-03-04 20:31:12 -0800401 self.archive_stages[board] = archive_stage
402
David James944a48e2012-03-07 12:19:03 -0800403 # Set up a process pool to run test/archive stages in the background.
404 # This process runs task(board) for each board added to the queue.
David James58e0c092012-03-04 20:31:12 -0800405 queue = multiprocessing.Queue()
406 task = self._RunBackgroundStagesForBoard
David James6450a0a2012-12-04 07:59:53 -0800407 with parallel.BackgroundTaskRunner(queue, task):
David James944a48e2012-03-07 12:19:03 -0800408 for board in self.build_config['boards']:
David James58e0c092012-03-04 20:31:12 -0800409 # Run BuildTarget in the foreground.
David James944a48e2012-03-07 12:19:03 -0800410 archive_stage = self.archive_stages[board]
411 config = configs.get(board, self.build_config)
412 self._RunStage(stages.BuildTargetStage, board, archive_stage,
Chris Sosa1a87b3e2012-04-12 13:20:42 -0700413 self.release_tag, config=config)
David James58e0c092012-03-04 20:31:12 -0800414 self.archive_urls[board] = archive_stage.GetDownloadUrl()
415
David James944a48e2012-03-07 12:19:03 -0800416 # Kick off task(board) in the background.
David James58e0c092012-03-04 20:31:12 -0800417 queue.put([board])
418
Brian Harring3fec5a82012-03-01 05:57:03 -0800419
420class DistributedBuilder(SimpleBuilder):
421 """Build class that has special logic to handle distributed builds.
422
423 These builds sync using git/manifest logic in manifest_versions. In general
424 they use a non-distributed builder code for the bulk of the work.
425 """
Ryan Cuif7f24692012-05-18 16:35:33 -0700426 def __init__(self, *args, **kwargs):
Brian Harring3fec5a82012-03-01 05:57:03 -0800427 """Initializes a buildbot builder.
428
429 Extra variables:
430 completion_stage_class: Stage used to complete a build. Set in the Sync
431 stage.
432 """
Ryan Cuif7f24692012-05-18 16:35:33 -0700433 super(DistributedBuilder, self).__init__(*args, **kwargs)
Brian Harring3fec5a82012-03-01 05:57:03 -0800434 self.completion_stage_class = None
435
436 def GetSyncInstance(self):
437 """Syncs the tree using one of the distributed sync logic paths.
438
439 Returns: the instance of the sync stage that was run.
440 """
441 # Determine sync class to use. CQ overrides PFQ bits so should check it
442 # first.
443 if cbuildbot_config.IsCQType(self.build_config['build_type']):
444 sync_stage = self._GetStageInstance(stages.CommitQueueSyncStage)
445 self.completion_stage_class = stages.CommitQueueCompletionStage
446 elif cbuildbot_config.IsPFQType(self.build_config['build_type']):
447 sync_stage = self._GetStageInstance(stages.LKGMCandidateSyncStage)
448 self.completion_stage_class = stages.LKGMCandidateSyncCompletionStage
449 else:
450 sync_stage = self._GetStageInstance(stages.ManifestVersionedSyncStage)
451 self.completion_stage_class = stages.ManifestVersionedSyncCompletionStage
452
453 return sync_stage
454
455 def Publish(self, was_build_successful):
456 """Completes build by publishing any required information."""
457 completion_stage = self._GetStageInstance(self.completion_stage_class,
458 was_build_successful)
459 completion_stage.Run()
460 name = completion_stage.name
461 if not results_lib.Results.WasStageSuccessful(name):
462 should_publish_changes = False
463 else:
464 should_publish_changes = (self.build_config['master'] and
465 was_build_successful)
466
467 if should_publish_changes:
468 self._RunStage(stages.PublishUprevChangesStage)
469
470 def RunStages(self):
471 """Runs simple builder logic and publishes information to overlays."""
472 was_build_successful = False
473 try:
David Jamesf55709e2012-03-13 09:10:15 -0700474 super(DistributedBuilder, self).RunStages()
475 was_build_successful = results_lib.Results.BuildSucceededSoFar()
Brian Harring3fec5a82012-03-01 05:57:03 -0800476 except SystemExit as ex:
477 # If a stage calls sys.exit(0), it's exiting with success, so that means
478 # we should mark ourselves as successful.
479 if ex.code == 0:
480 was_build_successful = True
481 raise
482 finally:
483 self.Publish(was_build_successful)
484
Brian Harring3fec5a82012-03-01 05:57:03 -0800485
486def _ConfirmBuildRoot(buildroot):
487 """Confirm with user the inferred buildroot, and mark it as confirmed."""
Brian Harring521e7242012-11-01 16:57:42 -0700488 cros_build_lib.Warning('Using default directory %s as buildroot', buildroot)
489 if not cros_build_lib.BooleanPrompt(default=False):
490 print('Please specify a different buildroot via the --buildroot option.')
Brian Harring3fec5a82012-03-01 05:57:03 -0800491 sys.exit(0)
492
493 if not os.path.exists(buildroot):
494 os.mkdir(buildroot)
495
496 repository.CreateTrybotMarker(buildroot)
497
498
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700499def _ConfirmRemoteBuildbotRun():
500 """Confirm user wants to run with --buildbot --remote."""
Brian Harring521e7242012-11-01 16:57:42 -0700501 cros_build_lib.Warning(
502 'You are about to launch a PRODUCTION job! This is *NOT* a '
503 'trybot run! Are you sure?')
504 if not cros_build_lib.BooleanPrompt(default=False):
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700505 print('Please specify --pass-through="--debug".')
506 sys.exit(0)
507
508
Ryan Cui5ba7e152012-05-10 14:36:52 -0700509def _DetermineDefaultBuildRoot(sourceroot, internal_build):
Brian Harring3fec5a82012-03-01 05:57:03 -0800510 """Default buildroot to be under the directory that contains current checkout.
511
512 Arguments:
513 internal_build: Whether the build is an internal build
Ryan Cui5ba7e152012-05-10 14:36:52 -0700514 sourceroot: Use specified sourceroot.
Brian Harring3fec5a82012-03-01 05:57:03 -0800515 """
Ryan Cui5ba7e152012-05-10 14:36:52 -0700516 if not repository.IsARepoRoot(sourceroot):
Brian Harring1b8c4c82012-05-29 23:03:04 -0700517 cros_build_lib.Die(
518 'Could not find root of local checkout at %s. Please specify '
519 'using the --sourceroot option.' % sourceroot)
Brian Harring3fec5a82012-03-01 05:57:03 -0800520
521 # Place trybot buildroot under the directory containing current checkout.
Ryan Cui5ba7e152012-05-10 14:36:52 -0700522 top_level = os.path.dirname(os.path.realpath(sourceroot))
Brian Harring3fec5a82012-03-01 05:57:03 -0800523 if internal_build:
524 buildroot = os.path.join(top_level, _DEFAULT_INT_BUILDROOT)
525 else:
526 buildroot = os.path.join(top_level, _DEFAULT_EXT_BUILDROOT)
527
528 return buildroot
529
530
531def _BackupPreviousLog(log_file, backup_limit=25):
532 """Rename previous log.
533
534 Args:
535 log_file: The absolute path to the previous log.
536 """
537 if os.path.exists(log_file):
538 old_logs = sorted(glob.glob(log_file + '.*'),
539 key=distutils.version.LooseVersion)
540
541 if len(old_logs) >= backup_limit:
542 os.remove(old_logs[0])
543
544 last = 0
545 if old_logs:
546 last = int(old_logs.pop().rpartition('.')[2])
547
548 os.rename(log_file, log_file + '.' + str(last + 1))
549
Ryan Cui5616a512012-08-17 13:39:36 -0700550
David James944a48e2012-03-07 12:19:03 -0800551def _RunBuildStagesWrapper(options, build_config):
Brian Harring3fec5a82012-03-01 05:57:03 -0800552 """Helper function that wraps RunBuildStages()."""
553 def IsDistributedBuilder():
554 """Determines whether the build_config should be a DistributedBuilder."""
555 if not options.buildbot:
556 return False
557 elif build_config['build_type'] in _DISTRIBUTED_TYPES:
558 chrome_rev = build_config['chrome_rev']
Chris Sosa52a81b62012-11-14 06:12:54 -0800559 if options.chrome_rev:
560 chrome_rev = options.chrome_rev
561
Brian Harring3fec5a82012-03-01 05:57:03 -0800562 # We don't do distributed logic to TOT Chrome PFQ's, nor local
563 # chrome roots (e.g. chrome try bots)
564 if chrome_rev not in [constants.CHROME_REV_TOT,
565 constants.CHROME_REV_LOCAL,
566 constants.CHROME_REV_SPEC]:
567 return True
568
569 return False
570
Brian Harring1b8c4c82012-05-29 23:03:04 -0700571 cros_build_lib.Info("cbuildbot executed with args %s"
572 % ' '.join(map(repr, sys.argv)))
Brian Harring3fec5a82012-03-01 05:57:03 -0800573
Ryan Cuif7f24692012-05-18 16:35:33 -0700574 target = DistributedBuilder if IsDistributedBuilder() else SimpleBuilder
Ryan Cuie1e4e662012-05-21 16:39:46 -0700575 buildbot = target(options, build_config)
Brian Harringd166aaf2012-05-14 18:31:53 -0700576 if not buildbot.Run():
577 sys.exit(1)
Brian Harring3fec5a82012-03-01 05:57:03 -0800578
579
580# Parser related functions
Ryan Cui5ba7e152012-05-10 14:36:52 -0700581def _CheckLocalPatches(sourceroot, local_patches):
Brian Harring3fec5a82012-03-01 05:57:03 -0800582 """Do an early quick check of the passed-in patches.
583
584 If the branch of a project is not specified we append the current branch the
585 project is on.
Ryan Cui5ba7e152012-05-10 14:36:52 -0700586
587 Args:
588 sourceroot: The checkout where patches are coming from.
Brian Harring3fec5a82012-03-01 05:57:03 -0800589 """
Ryan Cuicedd8a52012-03-22 02:28:35 -0700590 verified_patches = []
David James97d95872012-11-16 15:09:56 -0800591 manifest = git.ManifestCheckout.Cached(sourceroot)
Ryan Cuicedd8a52012-03-22 02:28:35 -0700592 for patch in local_patches:
Brian Harring3fec5a82012-03-01 05:57:03 -0800593 components = patch.split(':')
594 if len(components) > 2:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700595 cros_build_lib.Die(
596 'Specify local patches in project[:branch] format. Got %s' % patch)
Brian Harring3fec5a82012-03-01 05:57:03 -0800597
598 # validate project
599 project = components[0]
Brian Harring3fec5a82012-03-01 05:57:03 -0800600
Brian Harring609dc4e2012-05-07 02:17:44 -0700601 try:
602 project_dir = manifest.GetProjectPath(project, True)
603 except KeyError:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700604 cros_build_lib.Die('Project %s does not exist.' % project)
Brian Harring3fec5a82012-03-01 05:57:03 -0800605
606 # If no branch was specified, we use the project's current branch.
607 if len(components) == 1:
David James97d95872012-11-16 15:09:56 -0800608 branch = git.GetCurrentBranch(project_dir)
Brian Harring3fec5a82012-03-01 05:57:03 -0800609 if not branch:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700610 cros_build_lib.Die('Project %s is not on a branch!' % project)
Brian Harring3fec5a82012-03-01 05:57:03 -0800611 else:
612 branch = components[1]
David James97d95872012-11-16 15:09:56 -0800613 if not git.DoesLocalBranchExist(project_dir, branch):
Brian Harring1b8c4c82012-05-29 23:03:04 -0700614 cros_build_lib.Die('Project %s does not have branch %s'
615 % (project, branch))
Brian Harring3fec5a82012-03-01 05:57:03 -0800616
Brian Harring609dc4e2012-05-07 02:17:44 -0700617 verified_patches.append('%s:%s' % (project, branch))
Brian Harring3fec5a82012-03-01 05:57:03 -0800618
Ryan Cuicedd8a52012-03-22 02:28:35 -0700619 return verified_patches
Brian Harring3fec5a82012-03-01 05:57:03 -0800620
621
Brian Harring3fec5a82012-03-01 05:57:03 -0800622def _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."""
Brian Harring3fec5a82012-03-01 05:57:03 -0800634 if parser.values.chrome_rev is None:
635 parser.values.chrome_rev = constants.CHROME_REV_LOCAL
636
Ryan Cui5ba7e152012-05-10 14:36:52 -0700637 parser.values.chrome_root = value
Brian Harring3fec5a82012-03-01 05:57:03 -0800638
639
640def _CheckChromeRevOption(_option, _opt_str, value, parser):
641 """Validate the chrome_rev option."""
642 value = value.strip()
643 if value not in constants.VALID_CHROME_REVISIONS:
644 raise optparse.OptionValueError('Invalid chrome rev specified')
645
646 parser.values.chrome_rev = value
647
648
Brian Harringfec89fe2012-09-23 07:30:54 -0700649def _CheckGerritChromeOption(_option, _opt_str, _value, parser):
Ryan Cuif37608e2012-07-10 14:28:48 -0700650 """Validate the chrome_rev option."""
651 if parser.values.chrome_rev is None:
652 parser.values.chrome_rev = constants.CHROME_REV_TOT
653
654 parser.values.gerrit_chrome = True
655
656
Brian Harringae0a5322012-09-15 01:46:51 -0700657def FindCacheDir(parser, options):
658 if constants.SHARED_CACHE_ENVVAR in os.environ:
659 return commandline.OptionParser.FindCacheDir(parser, options)
660 return None
661
662
Ryan Cui5ba7e152012-05-10 14:36:52 -0700663class CustomGroup(optparse.OptionGroup):
664 def add_remote_option(self, *args, **kwargs):
665 """For arguments that are passed-through to remote trybot."""
666 return optparse.OptionGroup.add_option(self, *args,
667 remote_pass_through=True,
668 **kwargs)
669
670
Ryan Cui1c13a252012-10-16 15:00:16 -0700671class CustomOption(commandline.FilteringOption):
672 """Subclass FilteringOption class to implement pass-through and api."""
Ryan Cui5ba7e152012-05-10 14:36:52 -0700673
Ryan Cui1c13a252012-10-16 15:00:16 -0700674 ACTIONS = commandline.FilteringOption.ACTIONS + ('extend',)
675 STORE_ACTIONS = commandline.FilteringOption.STORE_ACTIONS + ('extend',)
676 TYPED_ACTIONS = commandline.FilteringOption.TYPED_ACTIONS + ('extend',)
677 ALWAYS_TYPED_ACTIONS = (commandline.FilteringOption.ALWAYS_TYPED_ACTIONS +
678 ('extend',))
Ryan Cui79319ab2012-05-21 12:59:18 -0700679
Ryan Cui5ba7e152012-05-10 14:36:52 -0700680 def __init__(self, *args, **kwargs):
681 # The remote_pass_through argument specifies whether we should directly
682 # pass the argument (with its value) onto the remote trybot.
683 self.pass_through = kwargs.pop('remote_pass_through', False)
Ryan Cui1c13a252012-10-16 15:00:16 -0700684 self.api_version = int(kwargs.pop('api', '0'))
685 commandline.FilteringOption.__init__(self, *args, **kwargs)
Ryan Cui5ba7e152012-05-10 14:36:52 -0700686
687 def take_action(self, action, dest, opt, value, values, parser):
Ryan Cui79319ab2012-05-21 12:59:18 -0700688 if action == 'extend':
Mike Frysingerd6925b52012-07-16 16:11:00 -0400689 # If there is extra spaces between each argument, we get '' which later
690 # code barfs on, so skip those. e.g. We see this with the forms:
691 # cbuildbot -p 'proj:branch ' ...
692 # cbuildbot -p ' proj:branch' ...
693 # cbuildbot -p 'proj:branch proj2:branch' ...
694 lvalue = value.split()
Ryan Cui79319ab2012-05-21 12:59:18 -0700695 values.ensure_value(dest, []).extend(lvalue)
Ryan Cui79319ab2012-05-21 12:59:18 -0700696
Ryan Cui1c13a252012-10-16 15:00:16 -0700697 commandline.FilteringOption.take_action(
698 self, action, dest, opt, value, values, parser)
Ryan Cui5ba7e152012-05-10 14:36:52 -0700699
700
Ryan Cui1c13a252012-10-16 15:00:16 -0700701class CustomParser(commandline.FilteringParser):
Brian Harringb6cf9142012-09-01 20:43:17 -0700702
703 DEFAULT_OPTION_CLASS = CustomOption
704
705 def add_remote_option(self, *args, **kwargs):
706 """For arguments that are passed-through to remote trybot."""
Ryan Cui1c13a252012-10-16 15:00:16 -0700707 return self.add_option(*args, remote_pass_through=True, **kwargs)
Brian Harringb6cf9142012-09-01 20:43:17 -0700708
709
Brian Harring3fec5a82012-03-01 05:57:03 -0800710def _CreateParser():
Ryan Cui16d9e1f2012-05-11 10:50:18 -0700711 """Generate and return the parser with all the options."""
Brian Harring3fec5a82012-03-01 05:57:03 -0800712 # Parse options
713 usage = "usage: %prog [options] buildbot_config"
Brian Harringae0a5322012-09-15 01:46:51 -0700714 parser = CustomParser(usage=usage, caching=FindCacheDir)
Brian Harring3fec5a82012-03-01 05:57:03 -0800715
716 # Main options
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700717 # The remote_pass_through parameter to add_option is implemented by the
718 # CustomOption class. See CustomOption for more information.
Brian Harring3fec5a82012-03-01 05:57:03 -0800719 parser.add_option('-a', '--all', action='store_true', dest='print_all',
720 default=False,
721 help=('List all of the buildbot configs available. Use '
722 'with the --list option'))
Ryan Cuie1e4e662012-05-21 16:39:46 -0700723 parser.add_remote_option('-b', '--branch',
724 help='The manifest branch to test. The branch to '
725 'check the buildroot out to.')
Ryan Cui5ba7e152012-05-10 14:36:52 -0700726 parser.add_option('-r', '--buildroot', dest='buildroot', type='path',
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700727 help='Root directory where source is checked out to, and '
728 'where the build occurs. For external build configs, '
729 "defaults to 'trybot' directory at top level of your "
730 'repo-managed checkout.')
731 parser.add_remote_option('--chrome_rev', default=None, type='string',
732 action='callback', dest='chrome_rev',
733 callback=_CheckChromeRevOption,
734 help=('Revision of Chrome to use, of type [%s]'
735 % '|'.join(constants.VALID_CHROME_REVISIONS)))
Ryan Cui79319ab2012-05-21 12:59:18 -0700736 parser.add_remote_option('-g', '--gerrit-patches', action='extend',
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700737 default=[], type='string',
738 metavar="'Id1 *int_Id2...IdN'",
739 help=("Space-separated list of short-form Gerrit "
740 "Change-Id's or change numbers to patch. "
741 "Please prepend '*' to internal Change-Id's"))
Brian Harring3fec5a82012-03-01 05:57:03 -0800742 parser.add_option('-l', '--list', action='store_true', dest='list',
743 default=False,
744 help=('List the suggested trybot configs to use. Use '
745 '--all to list all of the available configs.'))
Ryan Cui54da0702012-04-19 18:38:08 -0700746 parser.add_option('--local', default=False, action='store_true',
747 help=('Specifies that this tryjob should be run locally.'))
Ryan Cui79319ab2012-05-21 12:59:18 -0700748 parser.add_option('-p', '--local-patches', action='extend', default=[],
Brian Harring3fec5a82012-03-01 05:57:03 -0800749 metavar="'<project1>[:<branch1>]...<projectN>[:<branchN>]'",
750 help=('Space-separated list of project branches with '
751 'patches to apply. Projects are specified by name. '
752 'If no branch is specified the current branch of the '
753 'project will be used.'))
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700754 parser.add_remote_option('--profile', default=None, type='string',
755 action='store', dest='profile',
756 help='Name of profile to sub-specify board variant.')
Brian Harring3fec5a82012-03-01 05:57:03 -0800757 parser.add_option('--remote', default=False, action='store_true',
Brian Harring3fec5a82012-03-01 05:57:03 -0800758 help=('Specifies that this tryjob should be run remotely.'))
Brian Harring219a2b82012-07-18 15:30:12 -0700759 parser.add_option('--remote-description', default=None,
760 help=('Attach an optional description to a --remote run '
761 'to make it easier to identify the results when it '
762 'finishes.'))
Brian Harring3fec5a82012-03-01 05:57:03 -0800763
Ryan Cuif4f84be2012-07-09 18:50:41 -0700764 #
Brian Harring3fec5a82012-03-01 05:57:03 -0800765 # Advanced options
Ryan Cuif4f84be2012-07-09 18:50:41 -0700766 #
767
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700768 group = CustomGroup(
Brian Harring3fec5a82012-03-01 05:57:03 -0800769 parser,
770 'Advanced Options',
771 'Caution: use these options at your own risk.')
772
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700773 group.add_remote_option('--buildbot', dest='buildbot', action='store_true',
774 default=False, help='This is running on a buildbot')
775 group.add_remote_option('--buildnumber', help='build number', type='int',
776 default=0)
Ryan Cui5ba7e152012-05-10 14:36:52 -0700777 group.add_option('--chrome_root', default=None, type='path',
778 action='callback', callback=_CheckChromeRootOption,
779 dest='chrome_root', help='Local checkout of Chrome to use.')
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700780 group.add_remote_option('--chrome_version', default=None, type='string',
781 action='callback', dest='chrome_version',
782 callback=_CheckChromeVersionOption,
783 help='Used with SPEC logic to force a particular SVN '
784 'revision of chrome rather than the latest.')
785 group.add_remote_option('--clobber', action='store_true', dest='clobber',
786 default=False,
787 help='Clears an old checkout before syncing')
Yu-Ju Hong52134292012-06-28 12:50:42 -0700788 group.add_remote_option('--hwtest', dest='hwtest', action='store_true',
789 default=False,
790 help='This adds HW test for remote trybot')
Ryan Cui5ba7e152012-05-10 14:36:52 -0700791 parser.add_option('--log_dir', dest='log_dir', type='path',
Brian Harring3fec5a82012-03-01 05:57:03 -0800792 help=('Directory where logs are stored.'))
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700793 group.add_remote_option('--maxarchives', dest='max_archive_builds',
794 default=3, type='int',
795 help="Change the local saved build count limit.")
Ryan Cuibbd3d4b2012-08-17 12:20:37 -0700796 parser.add_remote_option('--manifest-repo-url',
797 help=('Overrides the default manifest repo url.'))
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700798 group.add_remote_option('--noarchive', action='store_false', dest='archive',
799 default=True, help="Don't run archive stage.")
Ryan Cuif7f24692012-05-18 16:35:33 -0700800 group.add_remote_option('--nobootstrap', action='store_false',
801 dest='bootstrap', default=True,
802 help="Don't checkout and run from a standalone "
803 "chromite repo.")
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700804 group.add_remote_option('--nobuild', action='store_false', dest='build',
805 default=True,
806 help="Don't actually build (for cbuildbot dev)")
807 group.add_remote_option('--noclean', action='store_false', dest='clean',
808 default=True, help="Don't clean the buildroot")
Ryan Cuif7f24692012-05-18 16:35:33 -0700809 group.add_remote_option('--nocgroups', action='store_false', dest='cgroups',
810 default=True,
811 help='Disable cbuildbots usage of cgroups.')
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700812 group.add_remote_option('--noprebuilts', action='store_false',
813 dest='prebuilts', default=True,
814 help="Don't upload prebuilts.")
815 group.add_remote_option('--nosync', action='store_false', dest='sync',
816 default=True, help="Don't sync before building.")
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700817 group.add_remote_option('--notests', action='store_false', dest='tests',
818 default=True,
819 help='Override values from buildconfig and run no '
820 'tests.')
821 group.add_remote_option('--nouprev', action='store_false', dest='uprev',
822 default=True,
823 help='Override values from buildconfig and never '
824 'uprev.')
Brian Harring3fec5a82012-03-01 05:57:03 -0800825 group.add_option('--reference-repo', action='store', default=None,
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700826 dest='reference_repo',
827 help='Reuse git data stored in an existing repo '
828 'checkout. This can drastically reduce the network '
829 'time spent setting up the trybot checkout. By '
830 "default, if this option isn't given but cbuildbot "
831 'is invoked from a repo checkout, cbuildbot will '
832 'use the repo root.')
Ryan Cuicedd8a52012-03-22 02:28:35 -0700833 group.add_option('--resume', action='store_true', default=False,
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700834 help='Skip stages already successfully completed.')
835 group.add_remote_option('--timeout', action='store', type='int', default=0,
836 help='Specify the maximum amount of time this job '
837 'can run for, at which point the build will be '
838 'aborted. If set to zero, then there is no '
839 'timeout.')
Ryan Cui39bdbbf2012-02-29 16:15:39 -0800840 group.add_option('--test-tryjob', action='store_true',
841 default=False,
842 help='Submit a tryjob to the test repository. Will not '
843 'show up on the production trybot waterfall.')
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700844 group.add_remote_option('--validation_pool', default=None,
845 help='Path to a pickled validation pool. Intended '
846 'for use only with the commit queue.')
847 group.add_remote_option('--version', dest='force_version', default=None,
848 help='Used with manifest logic. Forces use of this '
849 'version rather than create or get latest.')
Brian Harringab8fb5c2012-07-18 11:28:22 -0700850 group.add_remote_option('--cq-gerrit-query', dest='cq_gerrit_override',
851 default=None,
852 help=
853 "If given, this gerrit query will be used to find what patches to test, "
854 "rather than the normal 'CommitReady=2 AND Verified=1 AND CodeReview=2' "
855 "query it defaults to. Use with care- note additionally this setting "
856 "only has an effect if the buildbot target is a cq target, and we're "
857 "in buildbot mode.")
Brian Harring3fec5a82012-03-01 05:57:03 -0800858
859 parser.add_option_group(group)
860
Ryan Cuif4f84be2012-07-09 18:50:41 -0700861 #
862 # Hidden options.
863 #
864
865 # The base GS URL (gs://<bucket_name>/<path>) to archive artifacts to.
866 parser.add_remote_option('--archive-base', type='gs_path',
867 help=optparse.SUPPRESS_HELP)
868 # bootstrap-args are not verified by the bootstrap code. It gets passed
869 # direcly to the bootstrap re-execution.
870 parser.add_remote_option('--bootstrap-args', action='append',
871 default=[], help=optparse.SUPPRESS_HELP)
Ryan Cuif37608e2012-07-10 14:28:48 -0700872 # Specify to use Gerrit Source for building Chrome. Implies
873 # --chrome_rev=CHROME_REV_TOT.
874 parser.add_option('--gerrit-chrome', action='callback', default=False,
875 callback=_CheckGerritChromeOption, dest='gerrit_chrome',
876 help=optparse.SUPPRESS_HELP)
Ryan Cuif4f84be2012-07-09 18:50:41 -0700877 parser.add_option('--pass-through', dest='pass_through_args', action='append',
878 type='string', default=[], help=optparse.SUPPRESS_HELP)
879 # Used for handling forwards/backwards compatibility for --resume and
880 # --bootstrap.
881 parser.add_option('--reexec-api-version', dest='output_api_version',
882 action='store_true', default=False,
883 help=optparse.SUPPRESS_HELP)
884 # Indicates this is running on a remote trybot machine.
885 parser.add_option('--remote-trybot', dest='remote_trybot',
886 action='store_true', default=False,
887 help=optparse.SUPPRESS_HELP)
888 # Patches uploaded by trybot client when run using the -p option.
889 parser.add_remote_option('--remote-patches', action='extend', default=[],
890 help=optparse.SUPPRESS_HELP)
891 # Specify specific remote tryslaves to run on.
892 parser.add_option('--slaves', action='extend', default=[],
893 help=optparse.SUPPRESS_HELP)
894 parser.add_option('--sourceroot', type='path', default=constants.SOURCE_ROOT,
895 help=optparse.SUPPRESS_HELP)
896 # Causes cbuildbot to bootstrap itself twice, in the sequence A->B->C.
897 # A(unpatched) patches and bootstraps B. B patches and bootstraps C.
898 parser.add_remote_option('--test-bootstrap', action='store_true',
899 default=False, help=optparse.SUPPRESS_HELP)
Brian Harringf611e6e2012-07-17 18:47:44 -0700900 # Note the default here needs to be hardcoded to 3; that is the last version
901 # that lacked this functionality.
902 # This is used so that cbuildbot when processing tryjobs from
903 # older chromite instances, we can use it for handling compatibility.
904 parser.add_option('--remote-version', default=3, type=int, action='store',
905 help=optparse.SUPPRESS_HELP)
Ryan Cuif4f84be2012-07-09 18:50:41 -0700906
907 #
Brian Harring3fec5a82012-03-01 05:57:03 -0800908 # Debug options
Ryan Cuif4f84be2012-07-09 18:50:41 -0700909 #
Brian Harringfec89fe2012-09-23 07:30:54 -0700910 # Temporary hack; in place till --dry-run replaces --debug.
911 # pylint: disable=W0212
Brian Harring009db502012-10-10 02:21:37 -0700912 group = parser.debug_group
Brian Harringfec89fe2012-09-23 07:30:54 -0700913 debug = [x for x in group.option_list if x._long_opts == ['--debug']][0]
914 debug.help += " Currently functions as --dry-run in addition."
915 debug.pass_through = True
Brian Harring3fec5a82012-03-01 05:57:03 -0800916 group.add_option('--dump_config', action='store_true', dest='dump_config',
917 default=False,
918 help='Dump out build config options, and exit.')
919 group.add_option('--notee', action='store_false', dest='tee', default=True,
920 help="Disable logging and internal tee process. Primarily "
921 "used for debugging cbuildbot itself.")
Brian Harring3fec5a82012-03-01 05:57:03 -0800922 return parser
923
924
Ryan Cui85867972012-02-23 18:21:49 -0800925def _FinishParsing(options, args):
926 """Perform some parsing tasks that need to take place after optparse.
927
928 This function needs to be easily testable! Keep it free of
929 environment-dependent code. Put more detailed usage validation in
930 _PostParseCheck().
Brian Harring3fec5a82012-03-01 05:57:03 -0800931
932 Args:
Ryan Cui85867972012-02-23 18:21:49 -0800933 options, args: The options/args object returned by optparse
Brian Harring3fec5a82012-03-01 05:57:03 -0800934 """
Ryan Cui41023d92012-11-13 19:59:50 -0800935 # Populate options.pass_through_args.
936 accepted, _ = commandline.FilteringParser.FilterArgs(
937 options.parsed_args, lambda x: x.opt_inst.pass_through)
938 options.pass_through_args.extend(accepted)
Brian Harring07039b52012-05-13 17:56:47 -0700939
Brian Harring3fec5a82012-03-01 05:57:03 -0800940 if options.chrome_root:
941 if options.chrome_rev != constants.CHROME_REV_LOCAL:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700942 cros_build_lib.Die('Chrome rev must be %s if chrome_root is set.' %
943 constants.CHROME_REV_LOCAL)
Brian Harring3fec5a82012-03-01 05:57:03 -0800944 else:
945 if options.chrome_rev == constants.CHROME_REV_LOCAL:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700946 cros_build_lib.Die('Chrome root must be set if chrome_rev is %s.' %
947 constants.CHROME_REV_LOCAL)
Brian Harring3fec5a82012-03-01 05:57:03 -0800948
949 if options.chrome_version:
950 if options.chrome_rev != constants.CHROME_REV_SPEC:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700951 cros_build_lib.Die('Chrome rev must be %s if chrome_version is set.' %
952 constants.CHROME_REV_SPEC)
Brian Harring3fec5a82012-03-01 05:57:03 -0800953 else:
954 if options.chrome_rev == constants.CHROME_REV_SPEC:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700955 cros_build_lib.Die(
956 'Chrome rev must not be %s if chrome_version is not set.'
957 % constants.CHROME_REV_SPEC)
Brian Harring3fec5a82012-03-01 05:57:03 -0800958
Ryan Cuif37608e2012-07-10 14:28:48 -0700959 if options.gerrit_chrome:
960 if options.remote_trybot or options.remote:
961 cros_build_lib.Die('Cannot use --gerrit-chrome with remote trybots!')
962 elif options.chrome_rev != constants.CHROME_REV_TOT:
963 cros_build_lib.Die('Chrome rev must be %s if chrome_root is set.' %
964 constants.CHROME_REV_TOT)
965
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700966 patches = bool(options.gerrit_patches or options.local_patches)
967 if options.remote:
968 if options.local:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700969 cros_build_lib.Die('Cannot specify both --remote and --local')
Ryan Cui54da0702012-04-19 18:38:08 -0700970
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700971 if not options.buildbot and not patches:
Brian Harring7cc4b932012-11-01 16:58:55 -0700972 if not cros_build_lib.BooleanPrompt(
973 prompt="No patches were provided; are you sure you want to just "
974 "run a remote build of ToT?", default=False):
975 cros_build_lib.Die('Must provide patches when running with --remote.')
Brian Harring3fec5a82012-03-01 05:57:03 -0800976
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700977 # --debug needs to be explicitly passed through for remote invocations.
978 release_mode_with_patches = (options.buildbot and patches and
979 '--debug' not in options.pass_through_args)
980 else:
981 if len(args) > 1:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700982 cros_build_lib.Die('Multiple configs not supported if not running with '
Brian Harringf1aad832012-07-18 10:46:39 -0700983 '--remote. Got %r', args)
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700984
Ryan Cui79319ab2012-05-21 12:59:18 -0700985 if options.slaves:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700986 cros_build_lib.Die('Cannot use --slaves if not running with --remote.')
Ryan Cui79319ab2012-05-21 12:59:18 -0700987
Ryan Cuieaa9efd2012-04-25 17:56:45 -0700988 release_mode_with_patches = (options.buildbot and patches and
989 not options.debug)
990
David James5734ea32012-08-15 20:23:49 -0700991 # When running in release mode, make sure we are running with checked-in code.
992 # We want checked-in cbuildbot/scripts to prevent errors, and we want to build
993 # a release image with checked-in code for CrOS packages.
994 if release_mode_with_patches:
995 cros_build_lib.Die(
996 'Cannot provide patches when running with --buildbot!')
997
Ryan Cuiba41ad32012-03-08 17:15:29 -0800998 if options.buildbot and options.remote_trybot:
Brian Harring1b8c4c82012-05-29 23:03:04 -0700999 cros_build_lib.Die(
1000 '--buildbot and --remote-trybot cannot be used together.')
Ryan Cuiba41ad32012-03-08 17:15:29 -08001001
Ryan Cui85867972012-02-23 18:21:49 -08001002 # Record whether --debug was set explicitly vs. it was inferred.
1003 options.debug_forced = False
1004 if options.debug:
1005 options.debug_forced = True
1006 else:
Ryan Cui16ca5812012-03-08 20:34:27 -08001007 # We don't set debug by default for
1008 # 1. --buildbot invocations.
1009 # 2. --remote invocations, because it needs to push changes to the tryjob
1010 # repo.
1011 options.debug = not options.buildbot and not options.remote
Brian Harring3fec5a82012-03-01 05:57:03 -08001012
Ryan Cui1c13a252012-10-16 15:00:16 -07001013 # Record the configs targeted.
1014 options.build_targets = args[:]
1015
Brian Harring3fec5a82012-03-01 05:57:03 -08001016
Brian Harring1d7ba942012-04-24 06:37:18 -07001017# pylint: disable=W0613
Brian Harringae0a5322012-09-15 01:46:51 -07001018def _PostParseCheck(parser, options, args):
Ryan Cui85867972012-02-23 18:21:49 -08001019 """Perform some usage validation after we've parsed the arguments
Brian Harring3fec5a82012-03-01 05:57:03 -08001020
Ryan Cui85867972012-02-23 18:21:49 -08001021 Args:
1022 options/args: The options/args object returned by optparse
1023 """
Ryan Cuie1e4e662012-05-21 16:39:46 -07001024 if not options.branch:
David James97d95872012-11-16 15:09:56 -08001025 options.branch = git.GetChromiteTrackingBranch()
Ryan Cuie1e4e662012-05-21 16:39:46 -07001026
Brian Harringae0a5322012-09-15 01:46:51 -07001027 if not repository.IsARepoRoot(options.sourceroot):
1028 if options.local_patches:
1029 raise Exception('Could not find repo checkout at %s!'
1030 % options.sourceroot)
1031
1032 # Ensure we have a workable cachedir from this point forward.
1033 if options.cache_dir is None:
1034 # Note, options.sourceroot is set regardless of the path
1035 # actually existing.
1036 if os.path.exists(options.sourceroot):
1037 options.cache_dir = os.path.join(options.sourceroot, '.cache')
1038 elif options.buildroot is not None:
1039 options.cache_dir = os.path.join(options.buildroot, '.cache')
1040 else:
1041 options.cache_dir = parser.FindCacheDir(parser, options)
1042 options.cache_dir = os.path.abspath(options.cache_dir)
Brian Harring8c1d7b12012-10-04 17:36:32 -07001043 parser.ConfigureCacheDir(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001044
1045 osutils.SafeMakedirs(options.cache_dir)
Ryan Cui5ba7e152012-05-10 14:36:52 -07001046
Brian Harring609dc4e2012-05-07 02:17:44 -07001047 if options.local_patches:
Brian Harring1d7ba942012-04-24 06:37:18 -07001048 options.local_patches = _CheckLocalPatches(
Brian Harring609dc4e2012-05-07 02:17:44 -07001049 options.sourceroot, options.local_patches)
Brian Harring1d7ba942012-04-24 06:37:18 -07001050
1051 default = os.environ.get('CBUILDBOT_DEFAULT_MODE')
1052 if (default and not any([options.local, options.buildbot,
1053 options.remote, options.remote_trybot])):
Brian Harring1b8c4c82012-05-29 23:03:04 -07001054 cros_build_lib.Info("CBUILDBOT_DEFAULT_MODE=%s env var detected, using it."
1055 % default)
Brian Harring1d7ba942012-04-24 06:37:18 -07001056 default = default.lower()
1057 if default == 'local':
1058 options.local = True
1059 elif default == 'remote':
1060 options.remote = True
1061 elif default == 'buildbot':
1062 options.buildbot = True
1063 else:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001064 cros_build_lib.Die("CBUILDBOT_DEFAULT_MODE value %s isn't supported. "
1065 % default)
Ryan Cui85867972012-02-23 18:21:49 -08001066
1067
1068def _ParseCommandLine(parser, argv):
1069 """Completely parse the commandline arguments"""
Brian Harring3fec5a82012-03-01 05:57:03 -08001070 (options, args) = parser.parse_args(argv)
Brian Harring37e559b2012-05-22 20:47:32 -07001071
1072 if options.output_api_version:
Ryan Cui724f1d32012-10-15 18:10:22 -07001073 print constants.REEXEC_API_VERSION
Brian Harring37e559b2012-05-22 20:47:32 -07001074 sys.exit(0)
1075
Ryan Cui54da0702012-04-19 18:38:08 -07001076 if options.list:
1077 _PrintValidConfigs(options.print_all)
1078 sys.exit(0)
1079
Ryan Cui8be16062012-04-24 12:05:26 -07001080 # Strip out null arguments.
1081 # TODO(rcui): Remove when buildbot is fixed
1082 args = [arg for arg in args if arg]
1083 if not args:
1084 parser.error('Invalid usage. Use -h to see usage. Use -l to list '
1085 'supported configs.')
1086
Ryan Cui85867972012-02-23 18:21:49 -08001087 _FinishParsing(options, args)
1088 return options, args
1089
1090
1091def main(argv):
1092 # Set umask to 022 so files created by buildbot are readable.
1093 os.umask(022)
1094
Ryan Cui85867972012-02-23 18:21:49 -08001095 parser = _CreateParser()
1096 (options, args) = _ParseCommandLine(parser, argv)
Brian Harring3fec5a82012-03-01 05:57:03 -08001097
Brian Harringae0a5322012-09-15 01:46:51 -07001098 _PostParseCheck(parser, options, args)
Brian Harring3fec5a82012-03-01 05:57:03 -08001099
Mike Frysinger8fd67dc2012-12-03 23:51:18 -05001100 cros_build_lib.AssertOutsideChroot()
Zdenek Behan98ec2fb2012-08-31 17:12:18 +02001101
Brian Harring3fec5a82012-03-01 05:57:03 -08001102 if options.remote:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001103 cros_build_lib.logger.setLevel(logging.WARNING)
Ryan Cui16ca5812012-03-08 20:34:27 -08001104
Brian Harring3fec5a82012-03-01 05:57:03 -08001105 # Verify configs are valid.
1106 for bot in args:
1107 _GetConfig(bot)
1108
1109 # Verify gerrit patches are valid.
Ryan Cui16ca5812012-03-08 20:34:27 -08001110 print 'Verifying patches...'
Ryan Cuie1e4e662012-05-21 16:39:46 -07001111 patch_pool = AcquirePoolFromOptions(options)
Ryan Cui16d9e1f2012-05-11 10:50:18 -07001112
Ryan Cuieaa9efd2012-04-25 17:56:45 -07001113 # --debug need to be explicitly passed through for remote invocations.
1114 if options.buildbot and '--debug' not in options.pass_through_args:
1115 _ConfirmRemoteBuildbotRun()
1116
Ryan Cui16ca5812012-03-08 20:34:27 -08001117 print 'Submitting tryjob...'
Ryan Cui16d9e1f2012-05-11 10:50:18 -07001118 tryjob = remote_try.RemoteTryJob(options, args, patch_pool.local_patches)
Ryan Cuia25d8eb2012-07-11 14:54:27 -07001119 tryjob.Submit(testjob=options.test_tryjob, dryrun=False)
Ryan Cui16ca5812012-03-08 20:34:27 -08001120 print 'Tryjob submitted!'
1121 print ('Go to %s to view the status of your job.'
Ryan Cui4906e1c2012-04-03 20:09:34 -07001122 % tryjob.GetTrybotWaterfallLink())
Brian Harring3fec5a82012-03-01 05:57:03 -08001123 sys.exit(0)
Ryan Cui54da0702012-04-19 18:38:08 -07001124 elif (not options.buildbot and not options.remote_trybot
1125 and not options.resume and not options.local):
Brian Harring1b8c4c82012-05-29 23:03:04 -07001126 cros_build_lib.Warning(
1127 'Running in LOCAL TRYBOT mode! Use --remote to submit REMOTE '
1128 'tryjobs. Use --local to suppress this message.')
1129 cros_build_lib.Warning(
Ryan Cui51591352012-07-09 15:15:53 -07001130 'In the future, --local will be required to run the local '
Brian Harring1b8c4c82012-05-29 23:03:04 -07001131 'trybot.')
Ryan Cui54da0702012-04-19 18:38:08 -07001132 time.sleep(5)
Brian Harring3fec5a82012-03-01 05:57:03 -08001133
Ryan Cui8be16062012-04-24 12:05:26 -07001134 # Only expecting one config
1135 bot_id = args[-1]
1136 build_config = _GetConfig(bot_id)
Brian Harring3fec5a82012-03-01 05:57:03 -08001137
1138 if options.reference_repo is None:
Ryan Cui5ba7e152012-05-10 14:36:52 -07001139 repo_path = os.path.join(options.sourceroot, '.repo')
Brian Harring3fec5a82012-03-01 05:57:03 -08001140 # If we're being run from a repo checkout, reuse the repo's git pool to
1141 # cut down on sync time.
1142 if os.path.exists(repo_path):
Ryan Cui5ba7e152012-05-10 14:36:52 -07001143 options.reference_repo = options.sourceroot
Brian Harring3fec5a82012-03-01 05:57:03 -08001144 elif options.reference_repo:
1145 if not os.path.exists(options.reference_repo):
1146 parser.error('Reference path %s does not exist'
1147 % (options.reference_repo,))
1148 elif not os.path.exists(os.path.join(options.reference_repo, '.repo')):
1149 parser.error('Reference path %s does not look to be the base of a '
1150 'repo checkout; no .repo exists in the root.'
1151 % (options.reference_repo,))
Ryan Cuid4a24212012-04-04 18:08:12 -07001152
Brian Harringf11bf682012-05-14 15:53:43 -07001153 if (options.buildbot or options.remote_trybot) and not options.resume:
Brian Harring470f6112012-03-02 11:47:10 -08001154 if not options.cgroups:
Ryan Cuid4a24212012-04-04 18:08:12 -07001155 parser.error('Options --buildbot/--remote-trybot and --nocgroups cannot '
1156 'be used together. Cgroup support is required for '
1157 'buildbot/remote-trybot mode.')
Mike Frysingera78a56e2012-11-20 06:02:30 -05001158 if not cgroups.Cgroup.IsSupported():
Ryan Cuid4a24212012-04-04 18:08:12 -07001159 parser.error('Option --buildbot/--remote-trybot was given, but this '
1160 'system does not support cgroups. Failing.')
Brian Harring3fec5a82012-03-01 05:57:03 -08001161
David Jamesaad5cc72012-10-26 15:03:13 -07001162 missing = osutils.FindMissingBinaries(_BUILDBOT_REQUIRED_BINARIES)
Brian Harring351ce442012-03-09 16:38:14 -08001163 if missing:
Ryan Cuid4a24212012-04-04 18:08:12 -07001164 parser.error("Option --buildbot/--remote-trybot requires the following "
1165 "binaries which couldn't be found in $PATH: %s"
Brian Harring351ce442012-03-09 16:38:14 -08001166 % (', '.join(missing)))
1167
Brian Harring3fec5a82012-03-01 05:57:03 -08001168 if options.reference_repo:
1169 options.reference_repo = os.path.abspath(options.reference_repo)
1170
1171 if options.dump_config:
1172 # This works, but option ordering is bad...
1173 print 'Configuration %s:' % bot_id
1174 pretty_printer = pprint.PrettyPrinter(indent=2)
1175 pretty_printer.pprint(build_config)
1176 sys.exit(0)
1177
1178 if not options.buildroot:
1179 if options.buildbot:
1180 parser.error('Please specify a buildroot with the --buildroot option.')
Matt Tennantd55b1f42012-04-13 14:15:01 -07001181
Ryan Cui5ba7e152012-05-10 14:36:52 -07001182 options.buildroot = _DetermineDefaultBuildRoot(options.sourceroot,
1183 build_config['internal'])
Brian Harring470f6112012-03-02 11:47:10 -08001184 # We use a marker file in the buildroot to indicate the user has
1185 # consented to using this directory.
1186 if not os.path.exists(repository.GetTrybotMarkerPath(options.buildroot)):
1187 _ConfirmBuildRoot(options.buildroot)
Brian Harring3fec5a82012-03-01 05:57:03 -08001188
1189 # Sanity check of buildroot- specifically that it's not pointing into the
1190 # midst of an existing repo since git-repo doesn't support nesting.
Brian Harring3fec5a82012-03-01 05:57:03 -08001191 if (not repository.IsARepoRoot(options.buildroot) and
David James6b80dc62012-02-29 15:34:40 -08001192 repository.InARepoRepository(options.buildroot)):
Brian Harring3fec5a82012-03-01 05:57:03 -08001193 parser.error('Configured buildroot %s points into a repository checkout, '
1194 'rather than the root of it. This is not supported.'
1195 % options.buildroot)
1196
Chris Sosab5ea3b42012-10-25 15:25:20 -07001197 if not options.log_dir:
1198 options.log_dir = os.path.join(options.buildroot, _DEFAULT_LOG_DIR)
1199
Brian Harringd166aaf2012-05-14 18:31:53 -07001200 log_file = None
1201 if options.tee:
Chris Sosab5ea3b42012-10-25 15:25:20 -07001202 log_file = os.path.join(options.log_dir, _BUILDBOT_LOG_FILE)
1203 osutils.SafeMakedirs(options.log_dir)
Brian Harringd166aaf2012-05-14 18:31:53 -07001204 _BackupPreviousLog(log_file)
1205
Brian Harring1b8c4c82012-05-29 23:03:04 -07001206 with cros_build_lib.ContextManagerStack() as stack:
Brian Harringc2d09d92012-05-13 22:03:15 -07001207 critical_section = stack.Add(cleanup.EnforcedCleanupSection)
1208 stack.Add(sudo.SudoKeepAlive)
Brian Harringd166aaf2012-05-14 18:31:53 -07001209
Brian Harringc2d09d92012-05-13 22:03:15 -07001210 if not options.resume:
Brian Harring2bf55e12012-05-13 21:31:55 -07001211 # If we're in resume mode, use our parents tempdir rather than
1212 # nesting another layer.
Brian Harringc2d09d92012-05-13 22:03:15 -07001213 stack.Add(osutils.TempDirContextManager, 'cbuildbot-tmp')
1214 logging.debug("Cbuildbot tempdir is %r.", os.environ.get('TMP'))
Brian Harringd166aaf2012-05-14 18:31:53 -07001215
Brian Harringead69102012-07-31 15:54:07 -07001216 # TODO(ferringb): update this once https://gerrit.chromium.org/gerrit/25359
1217 # is landed- it's sensitive to the manifest-versions cache path.
Brian Harringae0a5322012-09-15 01:46:51 -07001218 options.preserve_paths = set(['manifest-versions', '.cache',
Brian Harringead69102012-07-31 15:54:07 -07001219 'manifest-versions-internal'])
Brian Harringd166aaf2012-05-14 18:31:53 -07001220 if log_file is not None:
1221 stack.Add(tee.Tee, log_file)
Brian Harring2d8f9ff2012-06-30 15:58:28 -07001222 options.preserve_paths.add(_DEFAULT_LOG_DIR)
Brian Harringd166aaf2012-05-14 18:31:53 -07001223
Brian Harringc2d09d92012-05-13 22:03:15 -07001224 if options.cgroups:
1225 stack.Add(cgroups.SimpleContainChildren, 'cbuildbot')
Brian Harringa184efa2012-03-04 11:51:25 -08001226
Brian Harringc2d09d92012-05-13 22:03:15 -07001227 # Mark everything between EnforcedCleanupSection and here as having to
1228 # be rolled back via the contextmanager cleanup handlers. This
1229 # ensures that sudo bits cannot outlive cbuildbot, that anything
1230 # cgroups would kill gets killed, etc.
1231 critical_section.ForkWatchdog()
Brian Harringd166aaf2012-05-14 18:31:53 -07001232
Brian Harringc2d09d92012-05-13 22:03:15 -07001233 if options.timeout > 0:
Brian Harring1b8c4c82012-05-29 23:03:04 -07001234 stack.Add(cros_build_lib.Timeout, options.timeout)
Brian Harringa184efa2012-03-04 11:51:25 -08001235
Brian Harringc2d09d92012-05-13 22:03:15 -07001236 if not options.buildbot:
1237 build_config = cbuildbot_config.OverrideConfigForTrybot(
1238 build_config,
1239 options.remote_trybot)
1240
1241 _RunBuildStagesWrapper(options, build_config)