3 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
7 """Main builder code for Chromium OS.
9 Used by Chromium OS buildbot configuration for all Chromium OS builds including
10 full and pre-flight-queue builds.
14 import distutils.version
19 import multiprocessing
25 from chromite.buildbot import cbuildbot_config
26 from chromite.buildbot import cbuildbot_stages as stages
27 from chromite.buildbot import cbuildbot_results as results_lib
28 from chromite.buildbot import cbuildbot_run
29 from chromite.buildbot import constants
30 from chromite.buildbot import manifest_version
31 from chromite.buildbot import remote_try
32 from chromite.buildbot import repository
33 from chromite.buildbot import tee
34 from chromite.buildbot import trybot_patch_pool
36 from chromite.lib import cgroups
37 from chromite.lib import cleanup
38 from chromite.lib import commandline
39 from chromite.lib import cros_build_lib
40 from chromite.lib import gclient
41 from chromite.lib import gerrit
42 from chromite.lib import git
43 from chromite.lib import osutils
44 from chromite.lib import patch as cros_patch
45 from chromite.lib import parallel
46 from chromite.lib import sudo
47 from chromite.lib import timeout_util
52 _DEFAULT_LOG_DIR = 'cbuildbot_logs'
53 _BUILDBOT_LOG_FILE = 'cbuildbot.log'
54 _DEFAULT_EXT_BUILDROOT = 'trybot'
55 _DEFAULT_INT_BUILDROOT = 'trybot-internal'
56 _BUILDBOT_REQUIRED_BINARIES = ('pbzip2',)
57 _API_VERSION_ATTR = 'api_version'
60 def _PrintValidConfigs(display_all=False):
61 """Print a list of valid buildbot configs.
64 display_all: Print all configs. Otherwise, prints only configs with
67 def _GetSortKey(config_name):
68 config_dict = cbuildbot_config.config[config_name]
69 return (not config_dict['trybot_list'], config_dict['description'],
74 print 'config'.ljust(COLUMN_WIDTH), 'description'
75 print '------'.ljust(COLUMN_WIDTH), '-----------'
76 config_names = cbuildbot_config.config.keys()
77 config_names.sort(key=_GetSortKey)
78 for name in config_names:
79 if display_all or cbuildbot_config.config[name]['trybot_list']:
80 desc = cbuildbot_config.config[name].get('description')
81 desc = desc if desc else ''
82 print name.ljust(COLUMN_WIDTH), desc
87 def _GetConfig(config_name):
88 """Gets the configuration for the build if it exists, None otherwise."""
89 if cbuildbot_config.config.has_key(config_name):
90 return cbuildbot_config.config[config_name]
93 def AcquirePoolFromOptions(options):
94 """Generate patch objects from passed in options.
97 options: The options object generated by optparse.
100 trybot_patch_pool.TrybotPatchPool object.
103 gerrit.GerritException, cros_patch.PatchException
109 if options.gerrit_patches:
110 gerrit_patches = gerrit.GetGerritPatchInfo(
111 options.gerrit_patches)
112 for patch in gerrit_patches:
113 if patch.IsAlreadyMerged():
114 cros_build_lib.Warning('Patch %s has already been merged.' % str(patch))
116 if options.local_patches:
117 manifest = git.ManifestCheckout.Cached(options.sourceroot)
118 local_patches = cros_patch.PrepareLocalPatches(manifest,
119 options.local_patches)
121 if options.remote_patches:
122 remote_patches = cros_patch.PrepareRemotePatches(
123 options.remote_patches)
125 return trybot_patch_pool.TrybotPatchPool(gerrit_patches, local_patches,
129 class Builder(object):
130 """Parent class for all builder types.
132 This class functions as an abstract parent class for various build types.
133 Its intended use is builder_instance.Run().
136 _run: The BuilderRun object for this run.
137 archive_stages: Dict of BuildConfig keys to ArchiveStage values.
138 patch_pool: TrybotPatchPool.
141 def __init__(self, builder_run):
142 """Initializes instance variables. Must be called by all subclasses."""
143 self._run = builder_run
145 if self._run.config.chromeos_official:
146 os.environ['CHROMEOS_OFFICIAL'] = '1'
148 self.archive_stages = {}
149 self.patch_pool = trybot_patch_pool.TrybotPatchPool()
150 self._build_image_lock = multiprocessing.Lock()
152 def Initialize(self):
153 """Runs through the initialization steps of an actual build."""
154 if self._run.options.resume:
155 results_lib.LoadCheckpoint(self._run.buildroot)
157 self._RunStage(stages.CleanUpStage)
159 def _GetStageInstance(self, stage, *args, **kwargs):
160 """Helper function to get a stage instance given the args.
162 Useful as almost all stages just take in builder_run.
164 # Normally the default BuilderRun (self._run) is used, but it can
165 # be overridden with "builder_run" kwargs (e.g. for child configs).
166 builder_run = kwargs.pop('builder_run', self._run)
167 return stage(builder_run, *args, **kwargs)
169 def _SetReleaseTag(self):
170 """Sets run.attrs.release_tag from the manifest manager used in sync.
172 Must be run after sync stage as syncing enables us to have a release tag,
173 and must be run before any usage of attrs.release_tag.
175 TODO(mtennant): Find a bottleneck place in syncing that can set this
176 directly. Be careful, as there are several kinds of syncing stages, and
177 sync stages have been known to abort with sys.exit calls.
179 manifest_manager = getattr(self._run.attrs, 'manifest_manager', None)
181 self._run.attrs.release_tag = manifest_manager.current_version
183 self._run.attrs.release_tag = None
185 cros_build_lib.Debug('Saved release_tag value for run: %r',
186 self._run.attrs.release_tag)
188 def _RunStage(self, stage, *args, **kwargs):
189 """Wrapper to run a stage.
192 stage: A BuilderStage class.
193 args: args to pass to stage constructor.
194 kwargs: kwargs to pass to stage constructor.
197 Whatever the stage's Run method returns.
199 stage_instance = self._GetStageInstance(stage, *args, **kwargs)
200 return stage_instance.Run()
203 def _RunParallelStages(stage_objs):
204 """Run the specified stages in parallel.
207 stage_objs: BuilderStage objects.
209 steps = [stage.Run for stage in stage_objs]
211 parallel.RunParallelSteps(steps)
213 except BaseException as ex:
214 # If a stage threw an exception, it might not have correctly reported
215 # results (e.g. because it was killed before it could report the
216 # results.) In this case, attribute the exception to any stages that
217 # didn't report back correctly (if any).
218 for stage in stage_objs:
219 for name in stage.GetStageNames():
220 if not results_lib.Results.StageHasResults(name):
221 results_lib.Results.Record(name, ex, str(ex))
225 def _RunSyncStage(self, sync_instance):
226 """Run given |sync_instance| stage and be sure attrs.release_tag set."""
230 self._SetReleaseTag()
232 def GetSyncInstance(self):
233 """Returns an instance of a SyncStage that should be run.
235 Subclasses must override this method.
237 raise NotImplementedError()
239 def GetCompletionInstance(self):
240 """Returns the MasterSlaveSyncCompletionStage for this build.
242 Subclasses may override this method.
250 """Subclasses must override this method. Runs the appropriate code."""
251 raise NotImplementedError()
253 def _ReExecuteInBuildroot(self, sync_instance):
254 """Reexecutes self in buildroot and returns True if build succeeds.
256 This allows the buildbot code to test itself when changes are patched for
257 buildbot-related code. This is a no-op if the buildroot == buildroot
258 of the running chromite checkout.
261 sync_instance: Instance of the sync stage that was run to sync.
264 True if the Build succeeded.
266 if not self._run.options.resume:
267 results_lib.WriteCheckpoint(self._run.options.buildroot)
269 args = stages.BootstrapStage.FilterArgsForTargetCbuildbot(
270 self._run.options.buildroot, constants.PATH_TO_CBUILDBOT,
273 # Specify a buildroot explicitly (just in case, for local trybot).
274 # Suppress any timeout options given from the commandline in the
275 # invoked cbuildbot; our timeout will enforce it instead.
276 args += ['--resume', '--timeout', '0', '--notee', '--nocgroups',
277 '--buildroot', os.path.abspath(self._run.options.buildroot)]
279 if hasattr(self._run.attrs, 'manifest_manager'):
280 # TODO(mtennant): Is this the same as self._run.attrs.release_tag?
281 ver = self._run.attrs.manifest_manager.current_version
282 args += ['--version', ver]
284 pool = getattr(sync_instance, 'pool', None)
286 filename = os.path.join(self._run.options.buildroot,
287 'validation_pool.dump')
289 args += ['--validation_pool', filename]
291 # Reset the cache dir so that the child will calculate it automatically.
292 if not self._run.options.cache_dir_specified:
293 commandline.BaseParser.ConfigureCacheDir(None)
295 # Re-run the command in the buildroot.
296 # Finally, be generous and give the invoked cbuildbot 30s to shutdown
297 # when something occurs. It should exit quicker, but the sigterm may
298 # hit while the system is particularly busy.
299 return_obj = cros_build_lib.RunCommand(
300 args, cwd=self._run.options.buildroot, error_code_ok=True,
302 return return_obj.returncode == 0
304 def _InitializeTrybotPatchPool(self):
305 """Generate patch pool from patches specified on the command line.
307 Do this only if we need to patch changes later on.
309 changes_stage = stages.PatchChangesStage.StageNamePrefix()
310 check_func = results_lib.Results.PreviouslyCompletedRecord
311 if not check_func(changes_stage) or self._run.options.bootstrap:
312 self.patch_pool = AcquirePoolFromOptions(self._run.options)
314 def _GetBootstrapStage(self):
315 """Constructs and returns the BootStrapStage object.
317 We return None when there are no chromite patches to test, and
318 --test-bootstrap wasn't passed in.
321 chromite_pool = self.patch_pool.Filter(project=constants.CHROMITE_PROJECT)
322 manifest_pool = self.patch_pool.FilterManifest()
323 chromite_branch = git.GetChromiteTrackingBranch()
324 if (chromite_pool or manifest_pool or
325 self._run.options.test_bootstrap or
326 chromite_branch != self._run.options.branch):
327 stage = stages.BootstrapStage(self._run, chromite_pool,
332 """Main runner for this builder class. Runs build and prints summary.
335 Whether the build succeeded.
337 self._InitializeTrybotPatchPool()
339 if self._run.options.bootstrap:
340 bootstrap_stage = self._GetBootstrapStage()
342 # BootstrapStage blocks on re-execution of cbuildbot.
343 bootstrap_stage.Run()
344 return bootstrap_stage.returncode == 0
347 exception_thrown = False
352 sync_instance = self.GetSyncInstance()
353 self._RunSyncStage(sync_instance)
355 if self._run.ShouldPatchAfterSync():
356 # Filter out patches to manifest, since PatchChangesStage can't handle
357 # them. Manifest patches are patched in the BootstrapStage.
358 non_manifest_patches = self.patch_pool.FilterManifest(negate=True)
359 if non_manifest_patches:
360 self._RunStage(stages.PatchChangesStage, non_manifest_patches)
362 if self._run.ShouldReexecAfterSync():
364 success = self._ReExecuteInBuildroot(sync_instance)
368 except Exception as ex:
369 # If the build is marked as successful, but threw exceptions, that's a
371 exception_thrown = True
372 if results_lib.Results.BuildSucceededSoFar():
373 traceback.print_exc(file=sys.stdout)
376 if not (print_report and isinstance(ex, results_lib.StepFailure)):
381 results_lib.WriteCheckpoint(self._run.options.buildroot)
382 completion_instance = self.GetCompletionInstance()
383 self._RunStage(stages.ReportStage, sync_instance, completion_instance)
384 success = results_lib.Results.BuildSucceededSoFar()
385 if exception_thrown and success:
387 cros_build_lib.PrintBuildbotStepWarnings()
389 Exception thrown, but all stages marked successful. This is an internal error,
390 because the stage that threw the exception should be marked as failing."""
395 BoardConfig = collections.namedtuple('BoardConfig', ['board', 'name'])
398 class SimpleBuilder(Builder):
399 """Builder that performs basic vetting operations."""
401 def GetSyncInstance(self):
402 """Sync to lkgm or TOT as necessary.
405 The instance of the sync stage to run.
407 if self._run.options.force_version:
408 sync_stage = self._GetStageInstance(stages.ManifestVersionedSyncStage)
409 elif self._run.config.use_lkgm:
410 sync_stage = self._GetStageInstance(stages.LKGMSyncStage)
411 elif self._run.config.use_chrome_lkgm:
412 sync_stage = self._GetStageInstance(stages.ChromeLKGMSyncStage)
414 sync_stage = self._GetStageInstance(stages.SyncStage)
418 def _RunHWTests(self, builder_run, board):
419 """Run hwtest-related stages for the specified board.
422 builder_run: BuilderRun object for these background stages.
425 # Upload HWTest artifacts first.
426 self._RunStage(stages.UploadTestArtifactsStage, board,
427 builder_run=builder_run)
429 # We can not run hw tests without archiving the payloads.
431 config = builder_run.config
432 if builder_run.options.archive:
433 for suite_config in config.hw_tests:
434 if suite_config.async:
435 stage_list.append([stages.ASyncHWTestStage, board, suite_config])
436 elif suite_config.suite == constants.HWTEST_AU_SUITE:
437 stage_list.append([stages.AUTestStage, board, suite_config])
438 elif suite_config.suite == constants.HWTEST_QAV_SUITE:
439 stage_list.append([stages.QATestStage, board, suite_config])
441 stage_list.append([stages.HWTestStage, board, suite_config])
443 stage_objs = [self._GetStageInstance(*x, builder_run=builder_run)
445 self._RunParallelStages(stage_objs)
447 def _RunBackgroundStagesForBoard(self, builder_run, board):
448 """Run background board-specific stages for the specified board.
451 builder_run: BuilderRun object for these background stages.
454 config = builder_run.config
456 # TODO(mtennant): This is the last usage of self.archive_stages. We can
457 # kill it once we migrate its uses to BuilderRun so that none of the
458 # stages below need it as an argument.
459 archive_stage = self.archive_stages[BoardConfig(board, config.name)]
460 if config.pgo_generate:
461 self._RunParallelStages([archive_stage])
464 # signer_results can't complete without push_image.
465 assert not config.signer_results or config.push_image
467 # paygen can't complete without signer_results.
468 assert not config.paygen or config.signer_results
470 if config.build_packages_in_background:
471 self._RunStage(stages.BuildPackagesStage, board, builder_run=builder_run)
473 if builder_run.config.compilecheck or builder_run.options.compilecheck:
474 self._RunStage(stages.UnitTestStage, board,
475 builder_run=builder_run)
478 # Build the image first before doing anything else.
479 # TODO(davidjames): Remove this lock once http://crbug.com/352994 is fixed.
480 with self._build_image_lock:
481 self._RunStage(stages.BuildImageStage, board, builder_run=builder_run,
482 pgo_use=config.pgo_use)
484 # While this stage list is run in parallel, the order here dictates the
485 # order that things will be shown in the log. So group things together
486 # that make sense when read in order. Also keep in mind that, since we
487 # gather output manually, early slow stages will prevent any output from
488 # later stages showing up until it finishes.
490 if builder_run.options.chrome_sdk and config.chrome_sdk:
491 stage_list.append([stages.ChromeSDKStage, board])
493 [stages.RetryStage, 1, stages.VMTestStage, board],
494 [stages.SignerTestStage, board, archive_stage],
495 [stages.SignerResultsStage, board, archive_stage],
496 [stages.PaygenStage, board, archive_stage],
497 [stages.UnitTestStage, board],
498 [stages.UploadPrebuiltsStage, board],
499 [stages.DevInstallerPrebuiltsStage, board],
500 [stages.DebugSymbolsStage, board],
501 [stages.CPEExportStage, board],
504 stage_objs = [self._GetStageInstance(*x, builder_run=builder_run)
507 parallel.RunParallelSteps([
508 lambda: self._RunParallelStages(stage_objs + [archive_stage]),
509 lambda: self._RunHWTests(builder_run, board),
512 def _RunSetupBoard(self):
513 """Run the SetupBoard stage for all child configs and boards."""
514 for builder_run in self._run.GetUngroupedBuilderRuns():
515 for board in builder_run.config.boards:
516 self._RunStage(stages.SetupBoardStage, board, builder_run=builder_run)
518 def _RunChrootBuilderTypeBuild(self):
519 """Runs through stages of a CHROOT_BUILDER_TYPE build."""
520 self._RunStage(stages.UprevStage, boards=[], enter_chroot=False)
521 self._RunStage(stages.InitSDKStage)
522 self._RunStage(stages.SetupBoardStage, constants.CHROOT_BUILDER_BOARD)
523 self._RunStage(stages.SyncChromeStage)
524 self._RunStage(stages.PatchChromeStage)
525 self._RunStage(stages.SDKPackageStage)
526 self._RunStage(stages.SDKTestStage)
527 self._RunStage(stages.UploadPrebuiltsStage, constants.CHROOT_BUILDER_BOARD)
529 def _RunRefreshPackagesTypeBuild(self):
530 """Runs through the stages of a REFRESH_PACKAGES_TYPE build."""
531 self._RunStage(stages.InitSDKStage)
532 self._RunSetupBoard()
533 self._RunStage(stages.RefreshPackageStatusStage)
535 def _RunMasterPaladinBuild(self):
536 """Runs through the stages of the paladin (commit queue) master build."""
537 self._RunStage(stages.InitSDKStage)
538 self._RunStage(stages.UprevStage)
539 # The CQ (paladin) master will not actually run the SyncChrome stage, but
540 # we want the logic that gets triggered when SyncChrome stage is skipped.
541 self._RunStage(stages.SyncChromeStage)
542 self._RunStage(stages.MasterUploadPrebuiltsStage)
544 def _RunPayloadsBuild(self):
545 """Run the PaygenStage once for each board."""
546 def _RunStageWrapper(board):
547 self._RunStage(stages.PaygenStage, board=board,
548 channels=self._run.options.channels, archive_stage=None)
550 with parallel.BackgroundTaskRunner(_RunStageWrapper) as queue:
551 for board in self._run.config.boards:
554 def _RunDefaultTypeBuild(self):
555 """Runs through the stages of a non-special-type build."""
556 self._RunStage(stages.InitSDKStage)
557 self._RunStage(stages.UprevStage)
558 self._RunSetupBoard()
559 self._RunStage(stages.SyncChromeStage)
560 self._RunStage(stages.PatchChromeStage)
562 # Prepare stages to run in background. If child_configs exist then
563 # run each of those here, otherwise use default config.
564 builder_runs = self._run.GetUngroupedBuilderRuns()
567 for builder_run in builder_runs:
568 # Prepare a local archive directory for each "run".
569 builder_run.GetArchive().SetupArchivePath()
571 for board in builder_run.config.boards:
572 archive_stage = self._GetStageInstance(
573 stages.ArchiveStage, board, builder_run=builder_run,
574 chrome_version=self._run.attrs.chrome_version)
575 board_config = BoardConfig(board, builder_run.config.name)
576 self.archive_stages[board_config] = archive_stage
577 tasks.append((builder_run, board))
579 # Set up a process pool to run test/archive stages in the background.
580 # This process runs task(board) for each board added to the queue.
581 task_runner = self._RunBackgroundStagesForBoard
582 with parallel.BackgroundTaskRunner(task_runner) as queue:
583 for builder_run, board in tasks:
584 if not builder_run.config.build_packages_in_background:
585 # Run BuildPackages in the foreground, generating or using PGO data
587 kwargs = {'builder_run': builder_run}
588 if builder_run.config.pgo_generate:
589 kwargs['pgo_generate'] = True
590 elif builder_run.config.pgo_use:
591 kwargs['pgo_use'] = True
593 self._RunStage(stages.BuildPackagesStage, board, **kwargs)
595 if builder_run.config.pgo_generate:
596 # Generate the PGO data before allowing any other tasks to run.
597 self._RunStage(stages.BuildImageStage, board, **kwargs)
598 self._RunStage(stages.UploadTestArtifactsStage, board,
599 builder_run=builder_run, suffix='[pgo_generate]')
600 suite = cbuildbot_config.PGORecordTest()
601 self._RunStage(stages.HWTestStage, board, suite,
602 builder_run=builder_run)
604 # Kick off our background stages.
605 queue.put([builder_run, board])
608 """Runs through build process."""
609 # TODO(sosa): Split these out into classes.
610 if self._run.config.build_type == constants.PRE_CQ_LAUNCHER_TYPE:
611 self._RunStage(stages.PreCQLauncherStage)
612 elif self._run.config.build_type == constants.CREATE_BRANCH_TYPE:
613 self._RunStage(stages.BranchUtilStage)
614 elif self._run.config.build_type == constants.CHROOT_BUILDER_TYPE:
615 self._RunChrootBuilderTypeBuild()
616 elif self._run.config.build_type == constants.REFRESH_PACKAGES_TYPE:
617 self._RunRefreshPackagesTypeBuild()
618 elif (self._run.config.build_type == constants.PALADIN_TYPE and
619 self._run.config.master):
620 self._RunMasterPaladinBuild()
621 elif self._run.config.build_type == constants.PAYLOADS_TYPE:
622 self._RunPayloadsBuild()
624 self._RunDefaultTypeBuild()
627 class DistributedBuilder(SimpleBuilder):
628 """Build class that has special logic to handle distributed builds.
630 These builds sync using git/manifest logic in manifest_versions. In general
631 they use a non-distributed builder code for the bulk of the work.
633 def __init__(self, *args, **kwargs):
634 """Initializes a buildbot builder.
637 completion_stage_class: Stage used to complete a build. Set in the Sync
640 super(DistributedBuilder, self).__init__(*args, **kwargs)
641 self.completion_stage_class = None
642 self.sync_stage = None
643 self._completion_stage = None
645 def GetSyncInstance(self):
646 """Syncs the tree using one of the distributed sync logic paths.
649 The instance of the sync stage to run.
651 # Determine sync class to use. CQ overrides PFQ bits so should check it
653 if self._run.config.pre_cq or self._run.options.pre_cq:
654 sync_stage = self._GetStageInstance(stages.PreCQSyncStage,
655 self.patch_pool.gerrit_patches)
656 self.completion_stage_class = stages.PreCQCompletionStage
657 self.patch_pool.gerrit_patches = []
658 elif cbuildbot_config.IsCQType(self._run.config.build_type):
659 if self._run.config.do_not_apply_cq_patches:
660 sync_stage = self._GetStageInstance(stages.MasterSlaveSyncStage)
662 sync_stage = self._GetStageInstance(stages.CommitQueueSyncStage)
663 self.completion_stage_class = stages.CommitQueueCompletionStage
664 elif cbuildbot_config.IsPFQType(self._run.config.build_type):
665 sync_stage = self._GetStageInstance(stages.MasterSlaveSyncStage)
666 self.completion_stage_class = stages.MasterSlaveSyncCompletionStage
668 sync_stage = self._GetStageInstance(stages.ManifestVersionedSyncStage)
669 self.completion_stage_class = stages.ManifestVersionedSyncCompletionStage
671 self.sync_stage = sync_stage
672 return self.sync_stage
674 def GetCompletionInstance(self):
675 """Returns the completion_stage_class instance that was used for this build.
678 None if the completion_stage instance was not yet created (this
679 occurs during Publish).
681 return self._completion_stage
683 def Publish(self, was_build_successful):
684 """Completes build by publishing any required information."""
685 completion_stage = self._GetStageInstance(self.completion_stage_class,
687 was_build_successful)
688 self._completion_stage = completion_stage
689 completion_successful = False
691 completion_stage.Run()
692 completion_successful = True
694 if not completion_successful:
695 was_build_successful = False
696 if self._run.config.push_overlays:
697 self._RunStage(stages.PublishUprevChangesStage, was_build_successful)
700 """Runs simple builder logic and publishes information to overlays."""
701 was_build_successful = False
703 super(DistributedBuilder, self).RunStages()
704 was_build_successful = results_lib.Results.BuildSucceededSoFar()
705 except SystemExit as ex:
706 # If a stage calls sys.exit(0), it's exiting with success, so that means
707 # we should mark ourselves as successful.
709 was_build_successful = True
712 self.Publish(was_build_successful)
715 def _ConfirmBuildRoot(buildroot):
716 """Confirm with user the inferred buildroot, and mark it as confirmed."""
717 cros_build_lib.Warning('Using default directory %s as buildroot', buildroot)
718 if not cros_build_lib.BooleanPrompt(default=False):
719 print('Please specify a different buildroot via the --buildroot option.')
722 if not os.path.exists(buildroot):
725 repository.CreateTrybotMarker(buildroot)
728 def _ConfirmRemoteBuildbotRun():
729 """Confirm user wants to run with --buildbot --remote."""
730 cros_build_lib.Warning(
731 'You are about to launch a PRODUCTION job! This is *NOT* a '
732 'trybot run! Are you sure?')
733 if not cros_build_lib.BooleanPrompt(default=False):
734 print('Please specify --pass-through="--debug".')
738 def _DetermineDefaultBuildRoot(sourceroot, internal_build):
739 """Default buildroot to be under the directory that contains current checkout.
742 internal_build: Whether the build is an internal build
743 sourceroot: Use specified sourceroot.
745 if not repository.IsARepoRoot(sourceroot):
747 'Could not find root of local checkout at %s. Please specify '
748 'using the --sourceroot option.' % sourceroot)
750 # Place trybot buildroot under the directory containing current checkout.
751 top_level = os.path.dirname(os.path.realpath(sourceroot))
753 buildroot = os.path.join(top_level, _DEFAULT_INT_BUILDROOT)
755 buildroot = os.path.join(top_level, _DEFAULT_EXT_BUILDROOT)
760 def _BackupPreviousLog(log_file, backup_limit=25):
761 """Rename previous log.
764 log_file: The absolute path to the previous log.
765 backup_limit: Maximum number of old logs to keep.
767 if os.path.exists(log_file):
768 old_logs = sorted(glob.glob(log_file + '.*'),
769 key=distutils.version.LooseVersion)
771 if len(old_logs) >= backup_limit:
772 os.remove(old_logs[0])
776 last = int(old_logs.pop().rpartition('.')[2])
778 os.rename(log_file, log_file + '.' + str(last + 1))
781 def _IsDistributedBuilder(options, chrome_rev, build_config):
782 """Determines whether the builder should be a DistributedBuilder.
785 options: options passed on the commandline.
786 chrome_rev: Chrome revision to build.
787 build_config: Builder configuration dictionary.
790 True if the builder should be a distributed_builder
792 if build_config['pre_cq'] or options.pre_cq:
794 elif not options.buildbot:
796 elif chrome_rev in (constants.CHROME_REV_TOT,
797 constants.CHROME_REV_LOCAL,
798 constants.CHROME_REV_SPEC):
799 # We don't do distributed logic to TOT Chrome PFQ's, nor local
800 # chrome roots (e.g. chrome try bots)
801 # TODO(davidjames): Update any builders that rely on this logic to use
802 # manifest_version=False instead.
804 elif build_config['manifest_version']:
810 def _RunBuildStagesWrapper(options, build_config):
811 """Helper function that wraps RunBuildStages()."""
812 cros_build_lib.Info('cbuildbot was executed with args %s' %
813 cros_build_lib.CmdToStr(sys.argv))
815 chrome_rev = build_config['chrome_rev']
816 if options.chrome_rev:
817 chrome_rev = options.chrome_rev
818 if chrome_rev == constants.CHROME_REV_TOT:
819 # Build the TOT Chrome revision.
820 svn_url = gclient.GetBaseURLs()[0]
821 options.chrome_version = gclient.GetTipOfTrunkSvnRevision(svn_url)
822 options.chrome_rev = constants.CHROME_REV_SPEC
824 # If it's likely we'll need to build Chrome, fetch the source.
825 if build_config['sync_chrome'] is None:
826 options.managed_chrome = (chrome_rev != constants.CHROME_REV_LOCAL and
827 (not build_config['usepkg_build_packages'] or chrome_rev or
828 build_config['profile'] or options.rietveld_patches))
830 options.managed_chrome = build_config['sync_chrome']
832 if options.managed_chrome:
833 # Tell Chrome to fetch the source locally.
834 internal = constants.USE_CHROME_INTERNAL in build_config['useflags']
835 chrome_src = 'chrome-src-internal' if internal else 'chrome-src'
836 options.chrome_root = os.path.join(options.cache_dir, 'distfiles', 'target',
838 elif options.rietveld_patches:
839 cros_build_lib.Die('This builder does not support Rietveld patches.')
841 # We are done munging options values, so freeze options object now to avoid
842 # further abuse of it.
843 # TODO(mtennant): one by one identify each options value override and see if
844 # it can be handled another way. Try to push this freeze closer and closer
845 # to the start of the script (e.g. in or after _PostParseCheck).
848 with parallel.Manager() as manager:
849 builder_run = cbuildbot_run.BuilderRun(options, build_config, manager)
850 if _IsDistributedBuilder(options, chrome_rev, build_config):
851 builder_cls = DistributedBuilder
853 builder_cls = SimpleBuilder
854 builder = builder_cls(builder_run)
855 if not builder.Run():
859 # Parser related functions
860 def _CheckLocalPatches(sourceroot, local_patches):
861 """Do an early quick check of the passed-in patches.
863 If the branch of a project is not specified we append the current branch the
866 TODO(davidjames): The project:branch format isn't unique, so this means that
867 we can't differentiate what directory the user intended to apply patches to.
868 We should references by directory instead.
871 sourceroot: The checkout where patches are coming from.
872 local_patches: List of patches to check in project:branch format.
875 A list of patches that have been verified, in project:branch format.
877 verified_patches = []
878 manifest = git.ManifestCheckout.Cached(sourceroot)
879 for patch in local_patches:
880 project, _, branch = patch.partition(':')
882 checkouts = manifest.FindCheckouts(project, only_patchable=True)
884 cros_build_lib.Die('Project %s does not exist.' % (project,))
885 if len(checkouts) > 1:
887 'We do not yet support local patching for projects that are checked '
888 'out to multiple directories. Try uploading your patch to gerrit '
889 'and referencing it via the -g option instead.'
893 for checkout in checkouts:
894 project_dir = checkout.GetPath(absolute=True)
896 # If no branch was specified, we use the project's current branch.
898 local_branch = git.GetCurrentBranch(project_dir)
900 local_branch = branch
902 if local_branch and git.DoesLocalBranchExist(project_dir, local_branch):
903 verified_patches.append('%s:%s' % (project, local_branch))
908 cros_build_lib.Die('Project %s does not have branch %s'
911 cros_build_lib.Die('Project %s is not on a branch!' % (project,))
913 return verified_patches
916 def _CheckChromeVersionOption(_option, _opt_str, value, parser):
917 """Upgrade other options based on chrome_version being passed."""
918 value = value.strip()
920 if parser.values.chrome_rev is None and value:
921 parser.values.chrome_rev = constants.CHROME_REV_SPEC
923 parser.values.chrome_version = value
926 def _CheckChromeRootOption(_option, _opt_str, value, parser):
927 """Validate and convert chrome_root to full-path form."""
928 if parser.values.chrome_rev is None:
929 parser.values.chrome_rev = constants.CHROME_REV_LOCAL
931 parser.values.chrome_root = value
934 def _CheckChromeRevOption(_option, _opt_str, value, parser):
935 """Validate the chrome_rev option."""
936 value = value.strip()
937 if value not in constants.VALID_CHROME_REVISIONS:
938 raise optparse.OptionValueError('Invalid chrome rev specified')
940 parser.values.chrome_rev = value
943 def FindCacheDir(_parser, _options):
947 class CustomGroup(optparse.OptionGroup):
948 """Custom option group which supports arguments passed-through to trybot."""
949 def add_remote_option(self, *args, **kwargs):
950 """For arguments that are passed-through to remote trybot."""
951 return optparse.OptionGroup.add_option(self, *args,
952 remote_pass_through=True,
956 class CustomOption(commandline.FilteringOption):
957 """Subclass FilteringOption class to implement pass-through and api."""
959 ACTIONS = commandline.FilteringOption.ACTIONS + ('extend',)
960 STORE_ACTIONS = commandline.FilteringOption.STORE_ACTIONS + ('extend',)
961 TYPED_ACTIONS = commandline.FilteringOption.TYPED_ACTIONS + ('extend',)
962 ALWAYS_TYPED_ACTIONS = (commandline.FilteringOption.ALWAYS_TYPED_ACTIONS +
965 def __init__(self, *args, **kwargs):
966 # The remote_pass_through argument specifies whether we should directly
967 # pass the argument (with its value) onto the remote trybot.
968 self.pass_through = kwargs.pop('remote_pass_through', False)
969 self.api_version = int(kwargs.pop('api', '0'))
970 commandline.FilteringOption.__init__(self, *args, **kwargs)
972 def take_action(self, action, dest, opt, value, values, parser):
973 if action == 'extend':
974 # If there is extra spaces between each argument, we get '' which later
975 # code barfs on, so skip those. e.g. We see this with the forms:
976 # cbuildbot -p 'proj:branch ' ...
977 # cbuildbot -p ' proj:branch' ...
978 # cbuildbot -p 'proj:branch proj2:branch' ...
979 lvalue = value.split()
980 values.ensure_value(dest, []).extend(lvalue)
982 commandline.FilteringOption.take_action(
983 self, action, dest, opt, value, values, parser)
986 class CustomParser(commandline.FilteringParser):
987 """Custom option parser which supports arguments passed-trhough to trybot"""
989 DEFAULT_OPTION_CLASS = CustomOption
991 def add_remote_option(self, *args, **kwargs):
992 """For arguments that are passed-through to remote trybot."""
993 return self.add_option(*args, remote_pass_through=True, **kwargs)
997 """Generate and return the parser with all the options."""
999 usage = "usage: %prog [options] buildbot_config [buildbot_config ...]"
1000 parser = CustomParser(usage=usage, caching=FindCacheDir)
1003 parser.add_option('-l', '--list', action='store_true', dest='list',
1005 help='List the suggested trybot configs to use (see --all)')
1006 parser.add_option('-a', '--all', action='store_true', dest='print_all',
1008 help='List all of the buildbot configs available w/--list')
1010 parser.add_option('--local', default=False, action='store_true',
1011 help='Specifies that this tryjob should be run locally. '
1013 parser.add_option('--remote', default=False, action='store_true',
1014 help='Specifies that this tryjob should be run remotely.')
1016 parser.add_remote_option('-b', '--branch',
1017 help='The manifest branch to test. The branch to '
1018 'check the buildroot out to.')
1019 parser.add_option('-r', '--buildroot', dest='buildroot', type='path',
1020 help='Root directory where source is checked out to, and '
1021 'where the build occurs. For external build configs, '
1022 "defaults to 'trybot' directory at top level of your "
1023 'repo-managed checkout.')
1024 parser.add_remote_option('--chrome_rev', default=None, type='string',
1025 action='callback', dest='chrome_rev',
1026 callback=_CheckChromeRevOption,
1027 help=('Revision of Chrome to use, of type [%s]'
1028 % '|'.join(constants.VALID_CHROME_REVISIONS)))
1029 parser.add_remote_option('--profile', default=None, type='string',
1030 action='store', dest='profile',
1031 help='Name of profile to sub-specify board variant.')
1034 # Patch selection options.
1037 group = CustomGroup(
1041 group.add_remote_option('-g', '--gerrit-patches', action='extend',
1042 default=[], type='string',
1043 metavar="'Id1 *int_Id2...IdN'",
1044 help="Space-separated list of short-form Gerrit "
1045 "Change-Id's or change numbers to patch. "
1046 "Please prepend '*' to internal Change-Id's")
1047 group.add_remote_option('-G', '--rietveld-patches', action='extend',
1048 default=[], type='string',
1049 metavar="'id1[:subdir1]...idN[:subdirN]'",
1050 help='Space-separated list of short-form Rietveld '
1051 'issue numbers to patch. If no subdir is '
1052 'specified, the src directory is used.')
1053 group.add_option('-p', '--local-patches', action='extend', default=[],
1054 metavar="'<project1>[:<branch1>]...<projectN>[:<branchN>]'",
1055 help='Space-separated list of project branches with '
1056 'patches to apply. Projects are specified by name. '
1057 'If no branch is specified the current branch of the '
1058 'project will be used.')
1060 parser.add_option_group(group)
1063 # Remote trybot options.
1066 group = CustomGroup(
1068 'Remote Trybot Options (--remote)')
1070 group.add_remote_option('--hwtest', dest='hwtest', action='store_true',
1072 help='Run the HWTest stage (tests on real hardware)')
1073 group.add_option('--remote-description', default=None,
1074 help='Attach an optional description to a --remote run '
1075 'to make it easier to identify the results when it '
1077 group.add_option('--slaves', action='extend', default=[],
1078 help='Specify specific remote tryslaves to run on (e.g. '
1079 'build149-m2); if the bot is busy, it will be queued')
1080 group.add_remote_option('--channel', dest='channels', action='extend',
1082 help='Specify a channel for a payloads trybot. Can be'
1083 'specified multiple times. No valid for '
1084 'non-payloads configs.')
1085 group.add_option('--test-tryjob', action='store_true',
1087 help='Submit a tryjob to the test repository. Will not '
1088 'show up on the production trybot waterfall.')
1090 parser.add_option_group(group)
1093 # Branch creation options.
1096 group = CustomGroup(
1098 'Branch Creation Options (used with branch-util)')
1100 group.add_remote_option('--branch-name',
1101 help='The branch to create or delete.')
1102 group.add_remote_option('--delete-branch', default=False, action='store_true',
1103 help='Delete the branch specified in --branch-name.')
1104 group.add_remote_option('--rename-to', type='string',
1105 help='Rename a branch to the specified name.')
1106 group.add_remote_option('--force-create', default=False, action='store_true',
1107 help='Overwrites an existing branch.')
1109 parser.add_option_group(group)
1115 group = CustomGroup(
1118 'Caution: use these options at your own risk.')
1120 group.add_remote_option('--bootstrap-args', action='append', default=[],
1121 help='Args passed directly to the bootstrap re-exec '
1122 'to skip verification by the bootstrap code')
1123 group.add_remote_option('--buildbot', dest='buildbot', action='store_true',
1124 default=False, help='This is running on a buildbot')
1125 group.add_remote_option('--buildnumber', help='build number', type='int',
1127 group.add_option('--chrome_root', default=None, type='path',
1128 action='callback', callback=_CheckChromeRootOption,
1129 dest='chrome_root', help='Local checkout of Chrome to use.')
1130 group.add_remote_option('--chrome_version', default=None, type='string',
1131 action='callback', dest='chrome_version',
1132 callback=_CheckChromeVersionOption,
1133 help='Used with SPEC logic to force a particular SVN '
1134 'revision of chrome rather than the latest.')
1135 group.add_remote_option('--clobber', action='store_true', dest='clobber',
1137 help='Clears an old checkout before syncing')
1138 group.add_remote_option('--latest-toolchain', action='store_true',
1140 help='Use the latest toolchain.')
1141 parser.add_option('--log_dir', dest='log_dir', type='path',
1142 help=('Directory where logs are stored.'))
1143 group.add_remote_option('--maxarchives', dest='max_archive_builds',
1144 default=3, type='int',
1145 help="Change the local saved build count limit.")
1146 parser.add_remote_option('--manifest-repo-url',
1147 help=('Overrides the default manifest repo url.'))
1148 group.add_remote_option('--compilecheck', action='store_true', default=False,
1149 help='Only verify compilation and unit tests.')
1150 group.add_remote_option('--noarchive', action='store_false', dest='archive',
1151 default=True, help="Don't run archive stage.")
1152 group.add_remote_option('--nobootstrap', action='store_false',
1153 dest='bootstrap', default=True,
1154 help="Don't checkout and run from a standalone "
1156 group.add_remote_option('--nobuild', action='store_false', dest='build',
1158 help="Don't actually build (for cbuildbot dev)")
1159 group.add_remote_option('--noclean', action='store_false', dest='clean',
1160 default=True, help="Don't clean the buildroot")
1161 group.add_remote_option('--nocgroups', action='store_false', dest='cgroups',
1163 help='Disable cbuildbots usage of cgroups.')
1164 group.add_remote_option('--nochromesdk', action='store_false',
1165 dest='chrome_sdk', default=True,
1166 help="Don't run the ChromeSDK stage which builds "
1167 "Chrome outside of the chroot.")
1168 group.add_remote_option('--noprebuilts', action='store_false',
1169 dest='prebuilts', default=True,
1170 help="Don't upload prebuilts.")
1171 group.add_remote_option('--nopatch', action='store_false',
1172 dest='postsync_patch', default=True,
1173 help=("Don't run PatchChanges stage. This does not "
1174 "disable patching in of chromite patches "
1175 "during BootstrapStage."))
1176 group.add_remote_option('--nopaygen', action='store_false',
1177 dest='paygen', default=True,
1178 help="Don't generate payloads.")
1179 group.add_remote_option('--noreexec', action='store_false',
1180 dest='postsync_reexec', default=True,
1181 help="Don't reexec into the buildroot after syncing.")
1182 group.add_remote_option('--nosdk', action='store_true',
1184 help='Re-create the SDK from scratch.')
1185 group.add_remote_option('--nosigner-results', action='store_false',
1186 dest='signer_results', default=True,
1187 help="Don't display signing results.")
1188 group.add_remote_option('--nosync', action='store_false', dest='sync',
1189 default=True, help="Don't sync before building.")
1190 group.add_remote_option('--notests', action='store_false', dest='tests',
1192 help='Override values from buildconfig and run no '
1194 group.add_remote_option('--nouprev', action='store_false', dest='uprev',
1196 help='Override values from buildconfig and never '
1198 group.add_option('--reference-repo', action='store', default=None,
1199 dest='reference_repo',
1200 help='Reuse git data stored in an existing repo '
1201 'checkout. This can drastically reduce the network '
1202 'time spent setting up the trybot checkout. By '
1203 "default, if this option isn't given but cbuildbot "
1204 'is invoked from a repo checkout, cbuildbot will '
1205 'use the repo root.')
1206 group.add_option('--resume', action='store_true', default=False,
1207 help='Skip stages already successfully completed.')
1208 group.add_remote_option('--timeout', action='store', type='int', default=0,
1209 help='Specify the maximum amount of time this job '
1210 'can run for, at which point the build will be '
1211 'aborted. If set to zero, then there is no '
1213 group.add_remote_option('--version', dest='force_version', default=None,
1214 help='Used with manifest logic. Forces use of this '
1215 'version rather than create or get latest. '
1216 'Examples: 4815.0.0-rc1, 4815.1.2')
1218 parser.add_option_group(group)
1224 group = CustomGroup(
1226 'Internal Chromium OS Build Team Options',
1227 'Caution: these are for meant for the Chromium OS build team only')
1229 group.add_remote_option('--archive-base', type='gs_path',
1230 help='Base GS URL (gs://<bucket_name>/<path>) to '
1231 'upload archive artifacts to')
1232 group.add_remote_option('--cq-gerrit-query', dest='cq_gerrit_override',
1235 "If given, this gerrit query will be used to find what patches to test, "
1236 "rather than the normal 'CommitQueue>=1 AND Verified=1 AND CodeReview=2' "
1237 "query it defaults to. Use with care- note additionally this setting "
1238 "only has an effect if the buildbot target is a cq target, and we're "
1239 "in buildbot mode.")
1240 group.add_option('--pass-through', dest='pass_through_args', action='append',
1241 type='string', default=[])
1242 group.add_remote_option('--pre-cq', action='store_true', default=False,
1243 help='Mark CLs as tested by the PreCQ on success.')
1244 group.add_option('--reexec-api-version', dest='output_api_version',
1245 action='store_true', default=False,
1246 help='Used for handling forwards/backwards compatibility '
1247 'with --resume and --bootstrap')
1248 group.add_option('--remote-trybot', dest='remote_trybot',
1249 action='store_true', default=False,
1250 help='Indicates this is running on a remote trybot machine')
1251 group.add_remote_option('--remote-patches', action='extend', default=[],
1252 help='Patches uploaded by the trybot client when run '
1253 'using the -p option')
1254 # Note the default here needs to be hardcoded to 3; that is the last version
1255 # that lacked this functionality.
1256 group.add_option('--remote-version', default=3, type=int, action='store',
1257 help='Used for compatibility checks w/tryjobs running in '
1258 'older chromite instances')
1259 group.add_option('--sourceroot', type='path', default=constants.SOURCE_ROOT)
1260 group.add_remote_option('--test-bootstrap', action='store_true',
1262 help='Causes cbuildbot to bootstrap itself twice, in '
1263 'the sequence A->B->C: A(unpatched) patches and '
1264 'bootstraps B; B patches and bootstraps C')
1265 group.add_remote_option('--validation_pool', default=None,
1266 help='Path to a pickled validation pool. Intended '
1267 'for use only with the commit queue.')
1268 group.add_remote_option('--mock-tree-status', dest='mock_tree_status',
1269 default=None, action='store',
1270 help='Override the tree status value that would be '
1271 'returned from the the actual tree. Example '
1272 'values: open, closed, throttled. When used '
1273 'in conjunction with --debug, the tree status '
1274 'will not be ignored as it usually is in a '
1276 group.add_remote_option('--mock-slave-status', dest='mock_slave_status',
1277 default=None, action='store',
1278 metavar='MOCK_SLAVE_STATUS_PICKLE_FILE',
1279 help='Override the result of the _FetchSlaveStatuses '
1280 'method of MasterSlaveSyncCompletionStage, by '
1281 'specifying a file with a pickle of the result '
1284 parser.add_option_group(group)
1289 # Temporary hack; in place till --dry-run replaces --debug.
1290 # pylint: disable=W0212
1291 group = parser.debug_group
1292 debug = [x for x in group.option_list if x._long_opts == ['--debug']][0]
1293 debug.help += " Currently functions as --dry-run in addition."
1294 debug.pass_through = True
1295 group.add_option('--notee', action='store_false', dest='tee', default=True,
1296 help="Disable logging and internal tee process. Primarily "
1297 "used for debugging cbuildbot itself.")
1301 def _FinishParsing(options, args):
1302 """Perform some parsing tasks that need to take place after optparse.
1304 This function needs to be easily testable! Keep it free of
1305 environment-dependent code. Put more detailed usage validation in
1309 options: The options object returned by optparse
1310 args: The args object returned by optparse
1312 # Populate options.pass_through_args.
1313 accepted, _ = commandline.FilteringParser.FilterArgs(
1314 options.parsed_args, lambda x: x.opt_inst.pass_through)
1315 options.pass_through_args.extend(accepted)
1317 if options.chrome_root:
1318 if options.chrome_rev != constants.CHROME_REV_LOCAL:
1319 cros_build_lib.Die('Chrome rev must be %s if chrome_root is set.' %
1320 constants.CHROME_REV_LOCAL)
1321 elif options.chrome_rev == constants.CHROME_REV_LOCAL:
1322 cros_build_lib.Die('Chrome root must be set if chrome_rev is %s.' %
1323 constants.CHROME_REV_LOCAL)
1325 if options.chrome_version:
1326 if options.chrome_rev != constants.CHROME_REV_SPEC:
1327 cros_build_lib.Die('Chrome rev must be %s if chrome_version is set.' %
1328 constants.CHROME_REV_SPEC)
1329 elif options.chrome_rev == constants.CHROME_REV_SPEC:
1331 'Chrome rev must not be %s if chrome_version is not set.'
1332 % constants.CHROME_REV_SPEC)
1334 patches = bool(options.gerrit_patches or options.local_patches or
1335 options.rietveld_patches)
1338 cros_build_lib.Die('Cannot specify both --remote and --local')
1340 if not options.buildbot and not patches:
1341 prompt = ('No patches were provided; are you sure you want to just '
1342 'run a remote build of %s?' % (
1343 options.branch if options.branch else 'ToT'))
1344 if not cros_build_lib.BooleanPrompt(prompt=prompt, default=False):
1345 cros_build_lib.Die('Must provide patches when running with --remote.')
1347 # --debug needs to be explicitly passed through for remote invocations.
1348 release_mode_with_patches = (options.buildbot and patches and
1349 '--debug' not in options.pass_through_args)
1352 cros_build_lib.Die('Multiple configs not supported if not running with '
1353 '--remote. Got %r', args)
1356 cros_build_lib.Die('Cannot use --slaves if not running with --remote.')
1358 release_mode_with_patches = (options.buildbot and patches and
1361 # When running in release mode, make sure we are running with checked-in code.
1362 # We want checked-in cbuildbot/scripts to prevent errors, and we want to build
1363 # a release image with checked-in code for CrOS packages.
1364 if release_mode_with_patches:
1366 'Cannot provide patches when running with --buildbot!')
1368 if options.buildbot and options.remote_trybot:
1370 '--buildbot and --remote-trybot cannot be used together.')
1372 # Record whether --debug was set explicitly vs. it was inferred.
1373 options.debug_forced = False
1375 options.debug_forced = True
1376 if not options.debug:
1377 # We don't set debug by default for
1378 # 1. --buildbot invocations.
1379 # 2. --remote invocations, because it needs to push changes to the tryjob
1381 options.debug = not options.buildbot and not options.remote
1383 # Record the configs targeted.
1384 options.build_targets = args[:]
1386 if constants.BRANCH_UTIL_CONFIG in options.build_targets:
1389 'Running %s as a remote tryjob is not yet supported.',
1390 constants.BRANCH_UTIL_CONFIG)
1391 if len(options.build_targets) > 1:
1393 'Cannot run %s with any other configs.',
1394 constants.BRANCH_UTIL_CONFIG)
1395 if not options.branch_name:
1397 'Must specify --branch-name with the %s config.',
1398 constants.BRANCH_UTIL_CONFIG)
1399 if options.branch and options.branch != options.branch_name:
1401 'If --branch is specified with the %s config, it must'
1402 ' have the same value as --branch-name.',
1403 constants.BRANCH_UTIL_CONFIG)
1405 exclusive_opts = {'--version': options.force_version,
1406 '--delete-branch': options.delete_branch,
1407 '--rename-to': options.rename_to,
1409 if 1 != sum(1 for x in exclusive_opts.values() if x):
1410 cros_build_lib.Die('When using the %s config, you must'
1411 ' specifiy one and only one of the following'
1412 ' options: %s.', constants.BRANCH_UTIL_CONFIG,
1413 ', '.join(exclusive_opts.keys()))
1415 # When deleting or renaming a branch, the --branch and --nobootstrap
1416 # options are implied.
1417 if options.delete_branch or options.rename_to:
1418 if not options.branch:
1419 cros_build_lib.Info('Automatically enabling sync to branch %s'
1420 ' for this %s flow.', options.branch_name,
1421 constants.BRANCH_UTIL_CONFIG)
1422 options.branch = options.branch_name
1423 if options.bootstrap:
1424 cros_build_lib.Info('Automatically disabling bootstrap step for'
1425 ' this %s flow.', constants.BRANCH_UTIL_CONFIG)
1426 options.bootstrap = False
1428 elif any([options.delete_branch, options.rename_to, options.branch_name]):
1430 'Cannot specify --delete-branch, --rename-to or --branch-name when not '
1431 'running the %s config', constants.BRANCH_UTIL_CONFIG)
1434 # pylint: disable=W0613
1435 def _PostParseCheck(parser, options, args):
1436 """Perform some usage validation after we've parsed the arguments
1439 parser: Option parser that was used to parse arguments.
1440 options: The options returned by optparse.
1441 args: The args returned by optparse.
1443 if not options.branch:
1444 options.branch = git.GetChromiteTrackingBranch()
1446 if not repository.IsARepoRoot(options.sourceroot):
1447 if options.local_patches:
1448 raise Exception('Could not find repo checkout at %s!'
1449 % options.sourceroot)
1451 # Because the default cache dir depends on other options, FindCacheDir
1452 # always returns None, and we setup the default here.
1453 if options.cache_dir is None:
1454 # Note, options.sourceroot is set regardless of the path
1455 # actually existing.
1456 if options.buildroot is not None:
1457 options.cache_dir = os.path.join(options.buildroot, '.cache')
1458 elif os.path.exists(options.sourceroot):
1459 options.cache_dir = os.path.join(options.sourceroot, '.cache')
1461 options.cache_dir = parser.FindCacheDir(parser, options)
1462 options.cache_dir = os.path.abspath(options.cache_dir)
1463 parser.ConfigureCacheDir(options.cache_dir)
1465 osutils.SafeMakedirsNonRoot(options.cache_dir)
1467 if options.local_patches:
1468 options.local_patches = _CheckLocalPatches(
1469 options.sourceroot, options.local_patches)
1471 default = os.environ.get('CBUILDBOT_DEFAULT_MODE')
1472 if (default and not any([options.local, options.buildbot,
1473 options.remote, options.remote_trybot])):
1474 cros_build_lib.Info("CBUILDBOT_DEFAULT_MODE=%s env var detected, using it."
1476 default = default.lower()
1477 if default == 'local':
1478 options.local = True
1479 elif default == 'remote':
1480 options.remote = True
1481 elif default == 'buildbot':
1482 options.buildbot = True
1484 cros_build_lib.Die("CBUILDBOT_DEFAULT_MODE value %s isn't supported. "
1487 # Ensure that all args are legitimate config targets.
1488 invalid_targets = []
1490 build_config = _GetConfig(arg)
1492 if not build_config:
1493 invalid_targets.append(arg)
1494 cros_build_lib.Error('No such configuraton target: "%s".', arg)
1497 if options.channels and build_config.build_type != constants.PAYLOADS_TYPE:
1498 cros_build_lib.Die('--channel must only be used with a payload config,'
1499 ' not target (%s).' % arg)
1501 # The --version option is not compatible with an external target unless the
1502 # --buildbot option is specified. More correctly, only "paladin versions"
1503 # will work with external targets, and those are only used with --buildbot.
1504 # If --buildbot is specified, then user should know what they are doing and
1505 # only specify a version that will work. See crbug.com/311648.
1506 if (options.force_version and
1507 not (options.buildbot or build_config.internal)):
1508 cros_build_lib.Die('Cannot specify --version without --buildbot for an'
1509 ' external target (%s).' % arg)
1512 cros_build_lib.Die('One or more invalid configuration targets specified. '
1513 'You can check the available configs by running '
1514 '`cbuildbot --list --all`')
1517 def _ParseCommandLine(parser, argv):
1518 """Completely parse the commandline arguments"""
1519 (options, args) = parser.parse_args(argv)
1521 # Strip out null arguments.
1522 # TODO(rcui): Remove when buildbot is fixed
1523 args = [arg for arg in args if arg]
1525 # A couple options, like --list, trigger a quick exit.
1526 if options.output_api_version:
1527 print constants.REEXEC_API_VERSION
1532 cros_build_lib.Die('No arguments expected with the --list options.')
1533 _PrintValidConfigs(options.print_all)
1537 parser.error('Invalid usage: no configuration targets provided.'
1538 'Use -h to see usage. Use -l to list supported configs.')
1540 _FinishParsing(options, args)
1541 return options, args
1544 # TODO(build): This function is too damn long.
1546 # Turn on strict sudo checks.
1547 cros_build_lib.STRICT_SUDO = True
1549 # Set umask to 022 so files created by buildbot are readable.
1552 parser = _CreateParser()
1553 (options, args) = _ParseCommandLine(parser, argv)
1555 _PostParseCheck(parser, options, args)
1557 cros_build_lib.AssertOutsideChroot()
1560 cros_build_lib.logger.setLevel(logging.WARNING)
1562 # Verify configs are valid.
1563 # If hwtest flag is enabled, post a warning that HWTest step may fail if the
1564 # specified board is not a released platform or it is a generic overlay.
1566 build_config = _GetConfig(bot)
1568 cros_build_lib.Warning(
1569 'If %s is not a released platform or it is a generic overlay, '
1570 'the HWTest step will most likely not run; please ask the lab '
1571 'team for help if this is unexpected.' % build_config['boards'])
1573 # Verify gerrit patches are valid.
1574 print 'Verifying patches...'
1575 patch_pool = AcquirePoolFromOptions(options)
1577 # --debug need to be explicitly passed through for remote invocations.
1578 if options.buildbot and '--debug' not in options.pass_through_args:
1579 _ConfirmRemoteBuildbotRun()
1581 print 'Submitting tryjob...'
1582 tryjob = remote_try.RemoteTryJob(options, args, patch_pool.local_patches)
1583 tryjob.Submit(testjob=options.test_tryjob, dryrun=False)
1584 print 'Tryjob submitted!'
1585 print ('Go to %s to view the status of your job.'
1586 % tryjob.GetTrybotWaterfallLink())
1589 elif (not options.buildbot and not options.remote_trybot
1590 and not options.resume and not options.local):
1591 options.local = True
1592 cros_build_lib.Warning(
1593 'Running in LOCAL TRYBOT mode! Use --remote to submit REMOTE '
1594 'tryjobs. Use --local to suppress this message.')
1595 cros_build_lib.Warning(
1596 'In the future, --local will be required to run the local '
1600 # Only one config arg is allowed in this mode, which was confirmed earlier.
1602 build_config = _GetConfig(bot_id)
1604 if options.reference_repo is None:
1605 repo_path = os.path.join(options.sourceroot, '.repo')
1606 # If we're being run from a repo checkout, reuse the repo's git pool to
1607 # cut down on sync time.
1608 if os.path.exists(repo_path):
1609 options.reference_repo = options.sourceroot
1610 elif options.reference_repo:
1611 if not os.path.exists(options.reference_repo):
1612 parser.error('Reference path %s does not exist'
1613 % (options.reference_repo,))
1614 elif not os.path.exists(os.path.join(options.reference_repo, '.repo')):
1615 parser.error('Reference path %s does not look to be the base of a '
1616 'repo checkout; no .repo exists in the root.'
1617 % (options.reference_repo,))
1619 if (options.buildbot or options.remote_trybot) and not options.resume:
1620 if not options.cgroups:
1621 parser.error('Options --buildbot/--remote-trybot and --nocgroups cannot '
1622 'be used together. Cgroup support is required for '
1623 'buildbot/remote-trybot mode.')
1624 if not cgroups.Cgroup.IsSupported():
1625 parser.error('Option --buildbot/--remote-trybot was given, but this '
1626 'system does not support cgroups. Failing.')
1628 missing = osutils.FindMissingBinaries(_BUILDBOT_REQUIRED_BINARIES)
1630 parser.error("Option --buildbot/--remote-trybot requires the following "
1631 "binaries which couldn't be found in $PATH: %s"
1632 % (', '.join(missing)))
1634 if options.reference_repo:
1635 options.reference_repo = os.path.abspath(options.reference_repo)
1637 if not options.buildroot:
1638 if options.buildbot:
1639 parser.error('Please specify a buildroot with the --buildbot option.')
1641 options.buildroot = _DetermineDefaultBuildRoot(options.sourceroot,
1642 build_config['internal'])
1643 # We use a marker file in the buildroot to indicate the user has
1644 # consented to using this directory.
1645 if not os.path.exists(repository.GetTrybotMarkerPath(options.buildroot)):
1646 _ConfirmBuildRoot(options.buildroot)
1648 # Sanity check of buildroot- specifically that it's not pointing into the
1649 # midst of an existing repo since git-repo doesn't support nesting.
1650 if (not repository.IsARepoRoot(options.buildroot) and
1651 git.FindRepoDir(options.buildroot)):
1652 parser.error('Configured buildroot %s points into a repository checkout, '
1653 'rather than the root of it. This is not supported.'
1654 % options.buildroot)
1656 if not options.log_dir:
1657 options.log_dir = os.path.join(options.buildroot, _DEFAULT_LOG_DIR)
1661 log_file = os.path.join(options.log_dir, _BUILDBOT_LOG_FILE)
1662 osutils.SafeMakedirs(options.log_dir)
1663 _BackupPreviousLog(log_file)
1665 with cros_build_lib.ContextManagerStack() as stack:
1666 # TODO(ferringb): update this once
1667 # https://chromium-review.googlesource.com/25359
1668 # is landed- it's sensitive to the manifest-versions cache path.
1669 options.preserve_paths = set(['manifest-versions', '.cache',
1670 'manifest-versions-internal'])
1671 if log_file is not None:
1672 # We don't want the critical section to try to clean up the tee process,
1673 # so we run Tee (forked off) outside of it. This prevents a deadlock
1674 # because the Tee process only exits when its pipe is closed, and the
1675 # critical section accidentally holds on to that file handle.
1676 stack.Add(tee.Tee, log_file)
1677 options.preserve_paths.add(_DEFAULT_LOG_DIR)
1679 critical_section = stack.Add(cleanup.EnforcedCleanupSection)
1680 stack.Add(sudo.SudoKeepAlive)
1682 if not options.resume:
1683 # If we're in resume mode, use our parents tempdir rather than
1684 # nesting another layer.
1685 stack.Add(osutils.TempDir, prefix='cbuildbot-tmp', set_global=True)
1686 logging.debug("Cbuildbot tempdir is %r.", os.environ.get('TMP'))
1689 stack.Add(cgroups.SimpleContainChildren, 'cbuildbot')
1691 # Mark everything between EnforcedCleanupSection and here as having to
1692 # be rolled back via the contextmanager cleanup handlers. This
1693 # ensures that sudo bits cannot outlive cbuildbot, that anything
1694 # cgroups would kill gets killed, etc.
1695 stack.Add(critical_section.ForkWatchdog)
1697 if options.timeout > 0:
1698 stack.Add(timeout_util.FatalTimeout, options.timeout)
1700 if not options.buildbot:
1701 build_config = cbuildbot_config.OverrideConfigForTrybot(
1702 build_config, options)
1704 if options.mock_tree_status is not None:
1705 stack.Add(mock.patch.object, timeout_util, '_GetStatus',
1706 return_value=options.mock_tree_status)
1708 if options.mock_slave_status is not None:
1709 with open(options.mock_slave_status, 'r') as f:
1710 mock_statuses = pickle.load(f)
1711 for key, value in mock_statuses.iteritems():
1712 mock_statuses[key] = manifest_version.BuilderStatus(**value)
1713 stack.Add(mock.patch.object, stages.MasterSlaveSyncCompletionStage,
1714 '_FetchSlaveStatuses', return_value=mock_statuses)
1716 _RunBuildStagesWrapper(options, build_config)