Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / chromite / scripts / cbuildbot.py
1 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Main builder code for Chromium OS.
6
7 Used by Chromium OS buildbot configuration for all Chromium OS builds including
8 full and pre-flight-queue builds.
9 """
10
11 from __future__ import print_function
12
13 import collections
14 import distutils.version
15 import glob
16 import json
17 import logging
18 import multiprocessing
19 import optparse
20 import os
21 import pickle
22 import sys
23 import tempfile
24 import traceback
25
26 from chromite.cbuildbot import afdo
27 from chromite.cbuildbot import cbuildbot_config
28 from chromite.cbuildbot import cbuildbot_run
29 from chromite.cbuildbot import constants
30 from chromite.cbuildbot import failures_lib
31 from chromite.cbuildbot import manifest_version
32 from chromite.cbuildbot import remote_try
33 from chromite.cbuildbot import repository
34 from chromite.cbuildbot import results_lib
35 from chromite.cbuildbot import tee
36 from chromite.cbuildbot import trybot_patch_pool
37 from chromite.cbuildbot.stages import afdo_stages
38 from chromite.cbuildbot.stages import artifact_stages
39 from chromite.cbuildbot.stages import branch_stages
40 from chromite.cbuildbot.stages import build_stages
41 from chromite.cbuildbot.stages import chrome_stages
42 from chromite.cbuildbot.stages import completion_stages
43 from chromite.cbuildbot.stages import generic_stages
44 from chromite.cbuildbot.stages import release_stages
45 from chromite.cbuildbot.stages import report_stages
46 from chromite.cbuildbot.stages import sdk_stages
47 from chromite.cbuildbot.stages import sync_stages
48 from chromite.cbuildbot.stages import test_stages
49
50
51 from chromite.lib import cidb
52 from chromite.lib import cgroups
53 from chromite.lib import cleanup
54 from chromite.lib import commandline
55 from chromite.lib import cros_build_lib
56 from chromite.lib import gerrit
57 from chromite.lib import git
58 from chromite.lib import gob_util
59 from chromite.lib import osutils
60 from chromite.lib import patch as cros_patch
61 from chromite.lib import parallel
62 from chromite.lib import sudo
63 from chromite.lib import timeout_util
64
65 import mock
66
67
68 _DEFAULT_LOG_DIR = 'cbuildbot_logs'
69 _BUILDBOT_LOG_FILE = 'cbuildbot.log'
70 _DEFAULT_EXT_BUILDROOT = 'trybot'
71 _DEFAULT_INT_BUILDROOT = 'trybot-internal'
72 _BUILDBOT_REQUIRED_BINARIES = ('pbzip2',)
73 _API_VERSION_ATTR = 'api_version'
74
75
76 def _PrintValidConfigs(display_all=False):
77   """Print a list of valid buildbot configs.
78
79   Args:
80     display_all: Print all configs.  Otherwise, prints only configs with
81                  trybot_list=True.
82   """
83   def _GetSortKey(config_name):
84     config_dict = cbuildbot_config.config[config_name]
85     return (not config_dict['trybot_list'], config_dict['description'],
86             config_name)
87
88   COLUMN_WIDTH = 45
89   print()
90   print('config'.ljust(COLUMN_WIDTH), 'description')
91   print('------'.ljust(COLUMN_WIDTH), '-----------')
92   config_names = cbuildbot_config.config.keys()
93   config_names.sort(key=_GetSortKey)
94   for name in config_names:
95     if display_all or cbuildbot_config.config[name]['trybot_list']:
96       desc = cbuildbot_config.config[name].get('description')
97       desc = desc if desc else ''
98       print(name.ljust(COLUMN_WIDTH), desc)
99
100   print()
101
102
103 def _GetConfig(config_name):
104   """Gets the configuration for the build if it exists, None otherwise."""
105   if cbuildbot_config.config.has_key(config_name):
106     return cbuildbot_config.config[config_name]
107
108
109 def AcquirePoolFromOptions(options):
110   """Generate patch objects from passed in options.
111
112   Args:
113     options: The options object generated by optparse.
114
115   Returns:
116     trybot_patch_pool.TrybotPatchPool object.
117
118   Raises:
119     gerrit.GerritException, cros_patch.PatchException
120   """
121   gerrit_patches = []
122   local_patches = []
123   remote_patches = []
124
125   if options.gerrit_patches:
126     gerrit_patches = gerrit.GetGerritPatchInfo(
127         options.gerrit_patches)
128     for patch in gerrit_patches:
129       if patch.IsAlreadyMerged():
130         cros_build_lib.Warning('Patch %s has already been merged.' % str(patch))
131
132   if options.local_patches:
133     manifest = git.ManifestCheckout.Cached(options.sourceroot)
134     local_patches = cros_patch.PrepareLocalPatches(manifest,
135                                                    options.local_patches)
136
137   if options.remote_patches:
138     remote_patches = cros_patch.PrepareRemotePatches(
139         options.remote_patches)
140
141   return trybot_patch_pool.TrybotPatchPool(gerrit_patches, local_patches,
142                                            remote_patches)
143
144
145 class Builder(object):
146   """Parent class for all builder types.
147
148   This class functions as an abstract parent class for various build types.
149   Its intended use is builder_instance.Run().
150
151   Attributes:
152     _run: The BuilderRun object for this run.
153     archive_stages: Dict of BuildConfig keys to ArchiveStage values.
154     patch_pool: TrybotPatchPool.
155   """
156
157   def __init__(self, builder_run):
158     """Initializes instance variables. Must be called by all subclasses."""
159     self._run = builder_run
160
161     if self._run.config.chromeos_official:
162       os.environ['CHROMEOS_OFFICIAL'] = '1'
163
164     self.archive_stages = {}
165     self.patch_pool = trybot_patch_pool.TrybotPatchPool()
166     self._build_image_lock = multiprocessing.Lock()
167
168   def Initialize(self):
169     """Runs through the initialization steps of an actual build."""
170     if self._run.options.resume:
171       results_lib.LoadCheckpoint(self._run.buildroot)
172
173     self._RunStage(report_stages.BuildStartStage)
174
175     self._RunStage(build_stages.CleanUpStage)
176
177   def _GetStageInstance(self, stage, *args, **kwargs):
178     """Helper function to get a stage instance given the args.
179
180     Useful as almost all stages just take in builder_run.
181     """
182     # Normally the default BuilderRun (self._run) is used, but it can
183     # be overridden with "builder_run" kwargs (e.g. for child configs).
184     builder_run = kwargs.pop('builder_run', self._run)
185     return stage(builder_run, *args, **kwargs)
186
187   def _SetReleaseTag(self):
188     """Sets run.attrs.release_tag from the manifest manager used in sync.
189
190     Must be run after sync stage as syncing enables us to have a release tag,
191     and must be run before any usage of attrs.release_tag.
192
193     TODO(mtennant): Find a bottleneck place in syncing that can set this
194     directly.  Be careful, as there are several kinds of syncing stages, and
195     sync stages have been known to abort with sys.exit calls.
196     """
197     manifest_manager = getattr(self._run.attrs, 'manifest_manager', None)
198     if manifest_manager:
199       self._run.attrs.release_tag = manifest_manager.current_version
200     else:
201       self._run.attrs.release_tag = None
202
203     cros_build_lib.Debug('Saved release_tag value for run: %r',
204                          self._run.attrs.release_tag)
205
206   def _RunStage(self, stage, *args, **kwargs):
207     """Wrapper to run a stage.
208
209     Args:
210       stage: A BuilderStage class.
211       args: args to pass to stage constructor.
212       kwargs: kwargs to pass to stage constructor.
213
214     Returns:
215       Whatever the stage's Run method returns.
216     """
217     stage_instance = self._GetStageInstance(stage, *args, **kwargs)
218     return stage_instance.Run()
219
220   @staticmethod
221   def _RunParallelStages(stage_objs):
222     """Run the specified stages in parallel.
223
224     Args:
225       stage_objs: BuilderStage objects.
226     """
227     steps = [stage.Run for stage in stage_objs]
228     try:
229       parallel.RunParallelSteps(steps)
230
231     except BaseException as ex:
232       # If a stage threw an exception, it might not have correctly reported
233       # results (e.g. because it was killed before it could report the
234       # results.) In this case, attribute the exception to any stages that
235       # didn't report back correctly (if any).
236       for stage in stage_objs:
237         for name in stage.GetStageNames():
238           if not results_lib.Results.StageHasResults(name):
239             results_lib.Results.Record(name, ex, str(ex))
240
241       raise
242
243   def _RunSyncStage(self, sync_instance):
244     """Run given |sync_instance| stage and be sure attrs.release_tag set."""
245     try:
246       sync_instance.Run()
247     finally:
248       self._SetReleaseTag()
249
250   def GetSyncInstance(self):
251     """Returns an instance of a SyncStage that should be run.
252
253     Subclasses must override this method.
254     """
255     raise NotImplementedError()
256
257   def GetCompletionInstance(self):
258     """Returns the MasterSlaveSyncCompletionStage for this build.
259
260     Subclasses may override this method.
261
262     Returns:
263       None
264     """
265     return None
266
267   def RunStages(self):
268     """Subclasses must override this method.  Runs the appropriate code."""
269     raise NotImplementedError()
270
271   def _ReExecuteInBuildroot(self, sync_instance):
272     """Reexecutes self in buildroot and returns True if build succeeds.
273
274     This allows the buildbot code to test itself when changes are patched for
275     buildbot-related code.  This is a no-op if the buildroot == buildroot
276     of the running chromite checkout.
277
278     Args:
279       sync_instance: Instance of the sync stage that was run to sync.
280
281     Returns:
282       True if the Build succeeded.
283     """
284     if not self._run.options.resume:
285       results_lib.WriteCheckpoint(self._run.options.buildroot)
286
287     args = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
288         self._run.options.buildroot, constants.PATH_TO_CBUILDBOT,
289         self._run.options)
290
291     # Specify a buildroot explicitly (just in case, for local trybot).
292     # Suppress any timeout options given from the commandline in the
293     # invoked cbuildbot; our timeout will enforce it instead.
294     args += ['--resume', '--timeout', '0', '--notee', '--nocgroups',
295              '--buildroot', os.path.abspath(self._run.options.buildroot)]
296
297     if hasattr(self._run.attrs, 'manifest_manager'):
298       # TODO(mtennant): Is this the same as self._run.attrs.release_tag?
299       ver = self._run.attrs.manifest_manager.current_version
300       args += ['--version', ver]
301
302     pool = getattr(sync_instance, 'pool', None)
303     if pool:
304       filename = os.path.join(self._run.options.buildroot,
305                               'validation_pool.dump')
306       pool.Save(filename)
307       args += ['--validation_pool', filename]
308
309     # Reset the cache dir so that the child will calculate it automatically.
310     if not self._run.options.cache_dir_specified:
311       commandline.BaseParser.ConfigureCacheDir(None)
312
313     with tempfile.NamedTemporaryFile(prefix='metadata') as metadata_file:
314       metadata_file.write(self._run.attrs.metadata.GetJSON())
315       metadata_file.flush()
316       args += ['--metadata_dump', metadata_file.name]
317
318       # Re-run the command in the buildroot.
319       # Finally, be generous and give the invoked cbuildbot 30s to shutdown
320       # when something occurs.  It should exit quicker, but the sigterm may
321       # hit while the system is particularly busy.
322       return_obj = cros_build_lib.RunCommand(
323           args, cwd=self._run.options.buildroot, error_code_ok=True,
324           kill_timeout=30)
325       return return_obj.returncode == 0
326
327   def _InitializeTrybotPatchPool(self):
328     """Generate patch pool from patches specified on the command line.
329
330     Do this only if we need to patch changes later on.
331     """
332     changes_stage = sync_stages.PatchChangesStage.StageNamePrefix()
333     check_func = results_lib.Results.PreviouslyCompletedRecord
334     if not check_func(changes_stage) or self._run.options.bootstrap:
335       self.patch_pool = AcquirePoolFromOptions(self._run.options)
336
337   def _GetBootstrapStage(self):
338     """Constructs and returns the BootStrapStage object.
339
340     We return None when there are no chromite patches to test, and
341     --test-bootstrap wasn't passed in.
342     """
343     stage = None
344     chromite_pool = self.patch_pool.Filter(project=constants.CHROMITE_PROJECT)
345     if self._run.config.internal:
346       manifest_pool = self.patch_pool.FilterIntManifest()
347     else:
348       manifest_pool = self.patch_pool.FilterExtManifest()
349     chromite_branch = git.GetChromiteTrackingBranch()
350     if (chromite_pool or manifest_pool or
351         self._run.options.test_bootstrap or
352         chromite_branch != self._run.options.branch):
353       stage = sync_stages.BootstrapStage(self._run, chromite_pool,
354                                          manifest_pool)
355     return stage
356
357   def Run(self):
358     """Main runner for this builder class.  Runs build and prints summary.
359
360     Returns:
361       Whether the build succeeded.
362     """
363     self._InitializeTrybotPatchPool()
364
365     if self._run.options.bootstrap:
366       bootstrap_stage = self._GetBootstrapStage()
367       if bootstrap_stage:
368         # BootstrapStage blocks on re-execution of cbuildbot.
369         bootstrap_stage.Run()
370         return bootstrap_stage.returncode == 0
371
372     print_report = True
373     exception_thrown = False
374     success = True
375     sync_instance = None
376     try:
377       self.Initialize()
378       sync_instance = self.GetSyncInstance()
379       self._RunSyncStage(sync_instance)
380
381       if self._run.ShouldPatchAfterSync():
382         # Filter out patches to manifest, since PatchChangesStage can't handle
383         # them.  Manifest patches are patched in the BootstrapStage.
384         non_manifest_patches = self.patch_pool.FilterManifest(negate=True)
385         if non_manifest_patches:
386           self._RunStage(sync_stages.PatchChangesStage, non_manifest_patches)
387
388       if self._run.ShouldReexecAfterSync():
389         print_report = False
390         success = self._ReExecuteInBuildroot(sync_instance)
391       else:
392         self._RunStage(report_stages.BuildReexecutionFinishedStage)
393         self.RunStages()
394
395     except Exception as ex:
396       exception_thrown = True
397       if results_lib.Results.BuildSucceededSoFar():
398         # If the build is marked as successful, but threw exceptions, that's a
399         # problem. Print the traceback for debugging.
400         if isinstance(ex, failures_lib.CompoundFailure):
401           print(str(ex))
402
403         traceback.print_exc(file=sys.stdout)
404         raise
405
406       if not (print_report and isinstance(ex, failures_lib.StepFailure)):
407         # If the failed build threw a non-StepFailure exception, we
408         # should raise it.
409         raise
410
411     finally:
412       if print_report:
413         results_lib.WriteCheckpoint(self._run.options.buildroot)
414         completion_instance = self.GetCompletionInstance()
415         self._RunStage(report_stages.ReportStage, sync_instance,
416                        completion_instance)
417         success = results_lib.Results.BuildSucceededSoFar()
418         if exception_thrown and success:
419           success = False
420           cros_build_lib.PrintBuildbotStepWarnings()
421           print("""\
422 Exception thrown, but all stages marked successful. This is an internal error,
423 because the stage that threw the exception should be marked as failing.""")
424
425     return success
426
427
428 BoardConfig = collections.namedtuple('BoardConfig', ['board', 'name'])
429
430
431 class SimpleBuilder(Builder):
432   """Builder that performs basic vetting operations."""
433
434   def GetSyncInstance(self):
435     """Sync to lkgm or TOT as necessary.
436
437     Returns:
438       The instance of the sync stage to run.
439     """
440     if self._run.options.force_version:
441       sync_stage = self._GetStageInstance(
442           sync_stages.ManifestVersionedSyncStage)
443     elif self._run.config.use_lkgm:
444       sync_stage = self._GetStageInstance(sync_stages.LKGMSyncStage)
445     elif self._run.config.use_chrome_lkgm:
446       sync_stage = self._GetStageInstance(chrome_stages.ChromeLKGMSyncStage)
447     else:
448       sync_stage = self._GetStageInstance(sync_stages.SyncStage)
449
450     return sync_stage
451
452   def _RunHWTests(self, builder_run, board):
453     """Run hwtest-related stages for the specified board.
454
455     Args:
456       builder_run: BuilderRun object for these background stages.
457       board: Board name.
458     """
459     # Upload HWTest artifacts first.
460     self._RunStage(artifact_stages.UploadTestArtifactsStage, board,
461                    builder_run=builder_run)
462
463     parallel_stages = []
464
465     # We can not run hw tests without archiving the payloads.
466     if builder_run.options.archive:
467       for suite_config in builder_run.config.hw_tests:
468         stage_class = None
469         if suite_config.async:
470           stage_class = test_stages.ASyncHWTestStage
471         elif suite_config.suite == constants.HWTEST_AU_SUITE:
472           stage_class = test_stages.AUTestStage
473         else:
474           stage_class = test_stages.HWTestStage
475         if suite_config.blocking:
476           self._RunStage(stage_class, board, suite_config,
477                          builder_run=builder_run)
478         else:
479           new_stage = self._GetStageInstance(stage_class, board,
480                                              suite_config,
481                                              builder_run=builder_run)
482           parallel_stages.append(new_stage)
483
484     self._RunParallelStages(parallel_stages)
485
486   def _RunBackgroundStagesForBoardAndMarkAsSuccessful(self, builder_run, board):
487     """Run background board-specific stages for the specified board.
488
489     After finishing the build, mark it as successful.
490
491     Args:
492       builder_run: BuilderRun object for these background stages.
493       board: Board name.
494     """
495     self._RunBackgroundStagesForBoard(builder_run, board)
496     board_runattrs = builder_run.GetBoardRunAttrs(board)
497     board_runattrs.SetParallel('success', True)
498
499   def _RunBackgroundStagesForBoard(self, builder_run, board):
500     """Run background board-specific stages for the specified board.
501
502     Used by _RunBackgroundStagesForBoardAndMarkAsSuccessful. Callers should use
503     that method instead.
504
505     Args:
506       builder_run: BuilderRun object for these background stages.
507       board: Board name.
508     """
509     config = builder_run.config
510
511     # TODO(mtennant): This is the last usage of self.archive_stages.  We can
512     # kill it once we migrate its uses to BuilderRun so that none of the
513     # stages below need it as an argument.
514     archive_stage = self.archive_stages[BoardConfig(board, config.name)]
515     if config.afdo_generate_min:
516       self._RunParallelStages([archive_stage])
517       return
518
519     # paygen can't complete without push_image.
520     assert not config.paygen or config.push_image
521
522     if config.build_packages_in_background:
523       self._RunStage(build_stages.BuildPackagesStage, board,
524                      update_metadata=True, builder_run=builder_run,
525                      afdo_use=config.afdo_use)
526
527     if builder_run.config.compilecheck or builder_run.options.compilecheck:
528       self._RunStage(test_stages.UnitTestStage, board,
529                      builder_run=builder_run)
530       return
531
532     # Build the image first before doing anything else.
533     # TODO(davidjames): Remove this lock once http://crbug.com/352994 is fixed.
534     with self._build_image_lock:
535       self._RunStage(build_stages.BuildImageStage, board,
536                      builder_run=builder_run, afdo_use=config.afdo_use)
537
538     # While this stage list is run in parallel, the order here dictates the
539     # order that things will be shown in the log.  So group things together
540     # that make sense when read in order.  Also keep in mind that, since we
541     # gather output manually, early slow stages will prevent any output from
542     # later stages showing up until it finishes.
543     stage_list = []
544     if builder_run.options.chrome_sdk and config.chrome_sdk:
545       stage_list.append([chrome_stages.ChromeSDKStage, board])
546
547     if config.vm_test_runs > 1:
548       # Run the VMTests multiple times to see if they fail.
549       stage_list += [
550           [generic_stages.RepeatStage, config.vm_test_runs,
551            test_stages.VMTestStage, board]]
552     else:
553       # Give the VMTests one retry attempt in case failures are flaky.
554       stage_list += [[generic_stages.RetryStage, 1, test_stages.VMTestStage,
555                       board]]
556
557     if config.afdo_generate:
558       stage_list += [[afdo_stages.AFDODataGenerateStage, board]]
559
560     stage_list += [
561         [release_stages.SignerTestStage, board, archive_stage],
562         [release_stages.PaygenStage, board, archive_stage],
563         [test_stages.ImageTestStage, board],
564         [test_stages.UnitTestStage, board],
565         [artifact_stages.UploadPrebuiltsStage, board],
566         [artifact_stages.DevInstallerPrebuiltsStage, board],
567         [artifact_stages.DebugSymbolsStage, board],
568         [artifact_stages.CPEExportStage, board],
569     ]
570
571     stage_objs = [self._GetStageInstance(*x, builder_run=builder_run)
572                   for x in stage_list]
573
574     parallel.RunParallelSteps([
575         lambda: self._RunParallelStages(stage_objs + [archive_stage]),
576         lambda: self._RunHWTests(builder_run, board),
577     ])
578
579   def _RunSetupBoard(self):
580     """Run the SetupBoard stage for all child configs and boards."""
581     for builder_run in self._run.GetUngroupedBuilderRuns():
582       for board in builder_run.config.boards:
583         self._RunStage(build_stages.SetupBoardStage, board,
584                        builder_run=builder_run)
585
586   def _RunChrootBuilderTypeBuild(self):
587     """Runs through stages of a CHROOT_BUILDER_TYPE build."""
588     self._RunStage(build_stages.UprevStage, boards=[], enter_chroot=False)
589     self._RunStage(build_stages.InitSDKStage)
590     self._RunStage(build_stages.SetupBoardStage, constants.CHROOT_BUILDER_BOARD)
591     self._RunStage(chrome_stages.SyncChromeStage)
592     self._RunStage(chrome_stages.PatchChromeStage)
593     self._RunStage(sdk_stages.SDKPackageStage)
594     self._RunStage(sdk_stages.SDKTestStage)
595     self._RunStage(artifact_stages.UploadPrebuiltsStage,
596                    constants.CHROOT_BUILDER_BOARD)
597
598   def _RunRefreshPackagesTypeBuild(self):
599     """Runs through the stages of a REFRESH_PACKAGES_TYPE build."""
600     self._RunStage(build_stages.InitSDKStage)
601     self._RunSetupBoard()
602     self._RunStage(report_stages.RefreshPackageStatusStage)
603
604   def _RunMasterPaladinOrChromePFQBuild(self):
605     """Runs through the stages of the paladin or chrome PFQ master build."""
606     self._RunStage(build_stages.InitSDKStage)
607     self._RunStage(build_stages.UprevStage)
608     # The CQ/Chrome PFQ master will not actually run the SyncChrome stage, but
609     # we want the logic that gets triggered when SyncChrome stage is skipped.
610     self._RunStage(chrome_stages.SyncChromeStage)
611     self._RunStage(artifact_stages.MasterUploadPrebuiltsStage)
612
613   def _RunPayloadsBuild(self):
614     """Run the PaygenStage once for each board."""
615     def _RunStageWrapper(board):
616       self._RunStage(release_stages.PaygenStage, board=board,
617                      channels=self._run.options.channels, archive_stage=None)
618
619     with parallel.BackgroundTaskRunner(_RunStageWrapper) as queue:
620       for board in self._run.config.boards:
621         queue.put([board])
622
623   def _RunDefaultTypeBuild(self):
624     """Runs through the stages of a non-special-type build."""
625     self._RunStage(build_stages.InitSDKStage)
626     self._RunStage(build_stages.UprevStage)
627     self._RunSetupBoard()
628     self._RunStage(chrome_stages.SyncChromeStage)
629     self._RunStage(chrome_stages.PatchChromeStage)
630
631     # Prepare stages to run in background.  If child_configs exist then
632     # run each of those here, otherwise use default config.
633     builder_runs = self._run.GetUngroupedBuilderRuns()
634
635     tasks = []
636     for builder_run in builder_runs:
637       # Prepare a local archive directory for each "run".
638       builder_run.GetArchive().SetupArchivePath()
639
640       for board in builder_run.config.boards:
641         archive_stage = self._GetStageInstance(
642             artifact_stages.ArchiveStage, board, builder_run=builder_run,
643             chrome_version=self._run.attrs.chrome_version)
644         board_config = BoardConfig(board, builder_run.config.name)
645         self.archive_stages[board_config] = archive_stage
646         tasks.append((builder_run, board))
647
648     # Set up a process pool to run test/archive stages in the background.
649     # This process runs task(board) for each board added to the queue.
650     task_runner = self._RunBackgroundStagesForBoardAndMarkAsSuccessful
651     with parallel.BackgroundTaskRunner(task_runner) as queue:
652       for builder_run, board in tasks:
653         if not builder_run.config.build_packages_in_background:
654           # Run BuildPackages in the foreground, generating or using AFDO data
655           # if requested.
656           kwargs = {'builder_run': builder_run}
657           if builder_run.config.afdo_generate_min:
658             kwargs['afdo_generate_min'] = True
659           elif builder_run.config.afdo_use:
660             kwargs['afdo_use'] = True
661
662           self._RunStage(build_stages.BuildPackagesStage, board,
663                          update_metadata=True, **kwargs)
664
665           if (builder_run.config.afdo_generate_min and
666               afdo.CanGenerateAFDOData(board)):
667             # Generate the AFDO data before allowing any other tasks to run.
668             self._RunStage(build_stages.BuildImageStage, board, **kwargs)
669             self._RunStage(artifact_stages.UploadTestArtifactsStage, board,
670                            builder_run=builder_run,
671                            suffix='[afdo_generate_min]')
672             suite = cbuildbot_config.AFDORecordTest()
673             self._RunStage(test_stages.HWTestStage, board, suite,
674                            builder_run=builder_run)
675             self._RunStage(afdo_stages.AFDODataGenerateStage, board,
676                            builder_run=builder_run)
677
678           if (builder_run.config.afdo_generate_min and
679               builder_run.config.afdo_update_ebuild):
680             self._RunStage(afdo_stages.AFDOUpdateEbuildStage,
681                            builder_run=builder_run)
682
683         # Kick off our background stages.
684         queue.put([builder_run, board])
685
686   def RunStages(self):
687     """Runs through build process."""
688     # TODO(sosa): Split these out into classes.
689     if self._run.config.build_type == constants.PRE_CQ_LAUNCHER_TYPE:
690       self._RunStage(sync_stages.PreCQLauncherStage)
691     elif self._run.config.build_type == constants.CREATE_BRANCH_TYPE:
692       self._RunStage(branch_stages.BranchUtilStage)
693     elif self._run.config.build_type == constants.CHROOT_BUILDER_TYPE:
694       self._RunChrootBuilderTypeBuild()
695     elif self._run.config.build_type == constants.REFRESH_PACKAGES_TYPE:
696       self._RunRefreshPackagesTypeBuild()
697     elif ((self._run.config.build_type == constants.PALADIN_TYPE or
698            self._run.config.build_type == constants.CHROME_PFQ_TYPE) and
699           self._run.config.master):
700       self._RunMasterPaladinOrChromePFQBuild()
701     elif self._run.config.build_type == constants.PAYLOADS_TYPE:
702       self._RunPayloadsBuild()
703     else:
704       self._RunDefaultTypeBuild()
705
706
707 class DistributedBuilder(SimpleBuilder):
708   """Build class that has special logic to handle distributed builds.
709
710   These builds sync using git/manifest logic in manifest_versions.  In general
711   they use a non-distributed builder code for the bulk of the work.
712   """
713
714   def __init__(self, *args, **kwargs):
715     """Initializes a buildbot builder.
716
717     Extra variables:
718       completion_stage_class:  Stage used to complete a build.  Set in the Sync
719         stage.
720     """
721     super(DistributedBuilder, self).__init__(*args, **kwargs)
722     self.completion_stage_class = None
723     self.sync_stage = None
724     self._completion_stage = None
725
726   def GetSyncInstance(self):
727     """Syncs the tree using one of the distributed sync logic paths.
728
729     Returns:
730       The instance of the sync stage to run.
731     """
732     # Determine sync class to use.  CQ overrides PFQ bits so should check it
733     # first.
734     if self._run.config.pre_cq or self._run.options.pre_cq:
735       sync_stage = self._GetStageInstance(sync_stages.PreCQSyncStage,
736                                           self.patch_pool.gerrit_patches)
737       self.completion_stage_class = completion_stages.PreCQCompletionStage
738       self.patch_pool.gerrit_patches = []
739     elif cbuildbot_config.IsCQType(self._run.config.build_type):
740       if self._run.config.do_not_apply_cq_patches:
741         sync_stage = self._GetStageInstance(
742             sync_stages.MasterSlaveLKGMSyncStage)
743       else:
744         sync_stage = self._GetStageInstance(sync_stages.CommitQueueSyncStage)
745       self.completion_stage_class = completion_stages.CommitQueueCompletionStage
746     elif cbuildbot_config.IsPFQType(self._run.config.build_type):
747       sync_stage = self._GetStageInstance(sync_stages.MasterSlaveLKGMSyncStage)
748       self.completion_stage_class = (
749           completion_stages.MasterSlaveSyncCompletionStage)
750     elif cbuildbot_config.IsCanaryType(self._run.config.build_type):
751       sync_stage = self._GetStageInstance(
752           sync_stages.ManifestVersionedSyncStage)
753       self.completion_stage_class = (
754           completion_stages.CanaryCompletionStage)
755     else:
756       sync_stage = self._GetStageInstance(
757           sync_stages.ManifestVersionedSyncStage)
758       self.completion_stage_class = (
759           completion_stages.ManifestVersionedSyncCompletionStage)
760
761     self.sync_stage = sync_stage
762     return self.sync_stage
763
764   def GetCompletionInstance(self):
765     """Returns the completion_stage_class instance that was used for this build.
766
767     Returns:
768       None if the completion_stage instance was not yet created (this
769       occurs during Publish).
770     """
771     return self._completion_stage
772
773   def Publish(self, was_build_successful, build_finished):
774     """Completes build by publishing any required information.
775
776     Args:
777       was_build_successful: Whether the build succeeded.
778       build_finished: Whether the build completed. A build can be successful
779         without completing if it exits early with sys.exit(0).
780     """
781     completion_stage = self._GetStageInstance(self.completion_stage_class,
782                                               self.sync_stage,
783                                               was_build_successful)
784     self._completion_stage = completion_stage
785     completion_successful = False
786     try:
787       completion_stage.Run()
788       completion_successful = True
789       if (self._run.config.afdo_update_ebuild and
790           not self._run.config.afdo_generate_min):
791         self._RunStage(afdo_stages.AFDOUpdateEbuildStage)
792     finally:
793       if self._run.config.push_overlays:
794         publish = (was_build_successful and completion_successful and
795                    build_finished)
796         self._RunStage(completion_stages.PublishUprevChangesStage, publish)
797
798   def RunStages(self):
799     """Runs simple builder logic and publishes information to overlays."""
800     was_build_successful = False
801     build_finished = False
802     try:
803       super(DistributedBuilder, self).RunStages()
804       was_build_successful = results_lib.Results.BuildSucceededSoFar()
805       build_finished = True
806     except SystemExit as ex:
807       # If a stage calls sys.exit(0), it's exiting with success, so that means
808       # we should mark ourselves as successful.
809       cros_build_lib.Info('Detected sys.exit(%s)', ex.code)
810       if ex.code == 0:
811         was_build_successful = True
812       raise
813     finally:
814       self.Publish(was_build_successful, build_finished)
815
816
817 def _ConfirmBuildRoot(buildroot):
818   """Confirm with user the inferred buildroot, and mark it as confirmed."""
819   cros_build_lib.Warning('Using default directory %s as buildroot', buildroot)
820   if not cros_build_lib.BooleanPrompt(default=False):
821     print('Please specify a different buildroot via the --buildroot option.')
822     sys.exit(0)
823
824   if not os.path.exists(buildroot):
825     os.mkdir(buildroot)
826
827   repository.CreateTrybotMarker(buildroot)
828
829
830 def _ConfirmRemoteBuildbotRun():
831   """Confirm user wants to run with --buildbot --remote."""
832   cros_build_lib.Warning(
833       'You are about to launch a PRODUCTION job!  This is *NOT* a '
834       'trybot run! Are you sure?')
835   if not cros_build_lib.BooleanPrompt(default=False):
836     print('Please specify --pass-through="--debug".')
837     sys.exit(0)
838
839
840 def _DetermineDefaultBuildRoot(sourceroot, internal_build):
841   """Default buildroot to be under the directory that contains current checkout.
842
843   Args:
844     internal_build: Whether the build is an internal build
845     sourceroot: Use specified sourceroot.
846   """
847   if not repository.IsARepoRoot(sourceroot):
848     cros_build_lib.Die(
849         'Could not find root of local checkout at %s.  Please specify '
850         'using the --sourceroot option.' % sourceroot)
851
852   # Place trybot buildroot under the directory containing current checkout.
853   top_level = os.path.dirname(os.path.realpath(sourceroot))
854   if internal_build:
855     buildroot = os.path.join(top_level, _DEFAULT_INT_BUILDROOT)
856   else:
857     buildroot = os.path.join(top_level, _DEFAULT_EXT_BUILDROOT)
858
859   return buildroot
860
861
862 def _BackupPreviousLog(log_file, backup_limit=25):
863   """Rename previous log.
864
865   Args:
866     log_file: The absolute path to the previous log.
867     backup_limit: Maximum number of old logs to keep.
868   """
869   if os.path.exists(log_file):
870     old_logs = sorted(glob.glob(log_file + '.*'),
871                       key=distutils.version.LooseVersion)
872
873     if len(old_logs) >= backup_limit:
874       os.remove(old_logs[0])
875
876     last = 0
877     if old_logs:
878       last = int(old_logs.pop().rpartition('.')[2])
879
880     os.rename(log_file, log_file + '.' + str(last + 1))
881
882
883 def _IsDistributedBuilder(options, chrome_rev, build_config):
884   """Determines whether the builder should be a DistributedBuilder.
885
886   Args:
887     options: options passed on the commandline.
888     chrome_rev: Chrome revision to build.
889     build_config: Builder configuration dictionary.
890
891   Returns:
892     True if the builder should be a distributed_builder
893   """
894   if build_config['pre_cq'] or options.pre_cq:
895     return True
896   elif not options.buildbot:
897     return False
898   elif chrome_rev in (constants.CHROME_REV_TOT,
899                       constants.CHROME_REV_LOCAL,
900                       constants.CHROME_REV_SPEC):
901     # We don't do distributed logic to TOT Chrome PFQ's, nor local
902     # chrome roots (e.g. chrome try bots)
903     # TODO(davidjames): Update any builders that rely on this logic to use
904     # manifest_version=False instead.
905     return False
906   elif build_config['manifest_version']:
907     return True
908
909   return False
910
911
912 def _RunBuildStagesWrapper(options, build_config):
913   """Helper function that wraps RunBuildStages()."""
914   cros_build_lib.Info('cbuildbot was executed with args %s' %
915                       cros_build_lib.CmdToStr(sys.argv))
916
917   chrome_rev = build_config['chrome_rev']
918   if options.chrome_rev:
919     chrome_rev = options.chrome_rev
920   if chrome_rev == constants.CHROME_REV_TOT:
921     options.chrome_version = gob_util.GetTipOfTrunkRevision(
922         constants.CHROMIUM_GOB_URL)
923     options.chrome_rev = constants.CHROME_REV_SPEC
924
925   # If it's likely we'll need to build Chrome, fetch the source.
926   if build_config['sync_chrome'] is None:
927     options.managed_chrome = (
928         chrome_rev != constants.CHROME_REV_LOCAL and
929         (not build_config['usepkg_build_packages'] or chrome_rev or
930          build_config['profile'] or options.rietveld_patches))
931   else:
932     options.managed_chrome = build_config['sync_chrome']
933
934   if options.managed_chrome:
935     # Tell Chrome to fetch the source locally.
936     internal = constants.USE_CHROME_INTERNAL in build_config['useflags']
937     chrome_src = 'chrome-src-internal' if internal else 'chrome-src'
938     options.chrome_root = os.path.join(options.cache_dir, 'distfiles', 'target',
939                                        chrome_src)
940   elif options.rietveld_patches:
941     cros_build_lib.Die('This builder does not support Rietveld patches.')
942
943   metadata_dump_dict = {}
944   if options.metadata_dump:
945     with open(options.metadata_dump, 'r') as metadata_file:
946       metadata_dump_dict = json.loads(metadata_file.read())
947
948   # We are done munging options values, so freeze options object now to avoid
949   # further abuse of it.
950   # TODO(mtennant): one by one identify each options value override and see if
951   # it can be handled another way.  Try to push this freeze closer and closer
952   # to the start of the script (e.g. in or after _PostParseCheck).
953   options.Freeze()
954
955   with parallel.Manager() as manager:
956     builder_run = cbuildbot_run.BuilderRun(options, build_config, manager)
957     if metadata_dump_dict:
958       builder_run.attrs.metadata.UpdateWithDict(metadata_dump_dict)
959     if _IsDistributedBuilder(options, chrome_rev, build_config):
960       builder_cls = DistributedBuilder
961     else:
962       builder_cls = SimpleBuilder
963     builder = builder_cls(builder_run)
964     if not builder.Run():
965       sys.exit(1)
966
967
968 # Parser related functions
969 def _CheckLocalPatches(sourceroot, local_patches):
970   """Do an early quick check of the passed-in patches.
971
972   If the branch of a project is not specified we append the current branch the
973   project is on.
974
975   TODO(davidjames): The project:branch format isn't unique, so this means that
976   we can't differentiate what directory the user intended to apply patches to.
977   We should references by directory instead.
978
979   Args:
980     sourceroot: The checkout where patches are coming from.
981     local_patches: List of patches to check in project:branch format.
982
983   Returns:
984     A list of patches that have been verified, in project:branch format.
985   """
986   verified_patches = []
987   manifest = git.ManifestCheckout.Cached(sourceroot)
988   for patch in local_patches:
989     project, _, branch = patch.partition(':')
990
991     checkouts = manifest.FindCheckouts(project, only_patchable=True)
992     if not checkouts:
993       cros_build_lib.Die('Project %s does not exist.' % (project,))
994     if len(checkouts) > 1:
995       cros_build_lib.Die(
996           'We do not yet support local patching for projects that are checked '
997           'out to multiple directories. Try uploading your patch to gerrit '
998           'and referencing it via the -g option instead.'
999       )
1000
1001     ok = False
1002     for checkout in checkouts:
1003       project_dir = checkout.GetPath(absolute=True)
1004
1005       # If no branch was specified, we use the project's current branch.
1006       if not branch:
1007         local_branch = git.GetCurrentBranch(project_dir)
1008       else:
1009         local_branch = branch
1010
1011       if local_branch and git.DoesCommitExistInRepo(project_dir, local_branch):
1012         verified_patches.append('%s:%s' % (project, local_branch))
1013         ok = True
1014
1015     if not ok:
1016       if branch:
1017         cros_build_lib.Die('Project %s does not have branch %s'
1018                            % (project, branch))
1019       else:
1020         cros_build_lib.Die('Project %s is not on a branch!' % (project,))
1021
1022   return verified_patches
1023
1024
1025 def _CheckChromeVersionOption(_option, _opt_str, value, parser):
1026   """Upgrade other options based on chrome_version being passed."""
1027   value = value.strip()
1028
1029   if parser.values.chrome_rev is None and value:
1030     parser.values.chrome_rev = constants.CHROME_REV_SPEC
1031
1032   parser.values.chrome_version = value
1033
1034
1035 def _CheckChromeRootOption(_option, _opt_str, value, parser):
1036   """Validate and convert chrome_root to full-path form."""
1037   if parser.values.chrome_rev is None:
1038     parser.values.chrome_rev = constants.CHROME_REV_LOCAL
1039
1040   parser.values.chrome_root = value
1041
1042
1043 def _CheckChromeRevOption(_option, _opt_str, value, parser):
1044   """Validate the chrome_rev option."""
1045   value = value.strip()
1046   if value not in constants.VALID_CHROME_REVISIONS:
1047     raise optparse.OptionValueError('Invalid chrome rev specified')
1048
1049   parser.values.chrome_rev = value
1050
1051
1052 def FindCacheDir(_parser, _options):
1053   return None
1054
1055
1056 class CustomGroup(optparse.OptionGroup):
1057   """Custom option group which supports arguments passed-through to trybot."""
1058
1059   def add_remote_option(self, *args, **kwargs):
1060     """For arguments that are passed-through to remote trybot."""
1061     return optparse.OptionGroup.add_option(self, *args,
1062                                            remote_pass_through=True,
1063                                            **kwargs)
1064
1065
1066 class CustomOption(commandline.FilteringOption):
1067   """Subclass FilteringOption class to implement pass-through and api."""
1068
1069   ACTIONS = commandline.FilteringOption.ACTIONS + ('extend',)
1070   STORE_ACTIONS = commandline.FilteringOption.STORE_ACTIONS + ('extend',)
1071   TYPED_ACTIONS = commandline.FilteringOption.TYPED_ACTIONS + ('extend',)
1072   ALWAYS_TYPED_ACTIONS = (commandline.FilteringOption.ALWAYS_TYPED_ACTIONS +
1073                           ('extend',))
1074
1075   def __init__(self, *args, **kwargs):
1076     # The remote_pass_through argument specifies whether we should directly
1077     # pass the argument (with its value) onto the remote trybot.
1078     self.pass_through = kwargs.pop('remote_pass_through', False)
1079     self.api_version = int(kwargs.pop('api', '0'))
1080     commandline.FilteringOption.__init__(self, *args, **kwargs)
1081
1082   def take_action(self, action, dest, opt, value, values, parser):
1083     if action == 'extend':
1084       # If there is extra spaces between each argument, we get '' which later
1085       # code barfs on, so skip those.  e.g. We see this with the forms:
1086       #  cbuildbot -p 'proj:branch ' ...
1087       #  cbuildbot -p ' proj:branch' ...
1088       #  cbuildbot -p 'proj:branch  proj2:branch' ...
1089       lvalue = value.split()
1090       values.ensure_value(dest, []).extend(lvalue)
1091
1092     commandline.FilteringOption.take_action(
1093         self, action, dest, opt, value, values, parser)
1094
1095
1096 class CustomParser(commandline.FilteringParser):
1097   """Custom option parser which supports arguments passed-trhough to trybot"""
1098
1099   DEFAULT_OPTION_CLASS = CustomOption
1100
1101   def add_remote_option(self, *args, **kwargs):
1102     """For arguments that are passed-through to remote trybot."""
1103     return self.add_option(*args, remote_pass_through=True, **kwargs)
1104
1105
1106 def _CreateParser():
1107   """Generate and return the parser with all the options."""
1108   # Parse options
1109   usage = 'usage: %prog [options] buildbot_config [buildbot_config ...]'
1110   parser = CustomParser(usage=usage, caching=FindCacheDir)
1111
1112   # Main options
1113   parser.add_option('-l', '--list', action='store_true', dest='list',
1114                     default=False,
1115                     help='List the suggested trybot configs to use (see --all)')
1116   parser.add_option('-a', '--all', action='store_true', dest='print_all',
1117                     default=False,
1118                     help='List all of the buildbot configs available w/--list')
1119
1120   parser.add_option('--local', default=False, action='store_true',
1121                     help=('Specifies that this tryjob should be run locally. '
1122                           'Implies --debug.'))
1123   parser.add_option('--remote', default=False, action='store_true',
1124                     help='Specifies that this tryjob should be run remotely.')
1125
1126   parser.add_remote_option('-b', '--branch',
1127                            help=('The manifest branch to test.  The branch to '
1128                                  'check the buildroot out to.'))
1129   parser.add_option('-r', '--buildroot', dest='buildroot', type='path',
1130                     help=('Root directory where source is checked out to, and '
1131                           'where the build occurs. For external build configs, '
1132                           "defaults to 'trybot' directory at top level of your "
1133                           'repo-managed checkout.'))
1134   parser.add_remote_option('--chrome_rev', default=None, type='string',
1135                            action='callback', dest='chrome_rev',
1136                            callback=_CheckChromeRevOption,
1137                            help=('Revision of Chrome to use, of type [%s]'
1138                                  % '|'.join(constants.VALID_CHROME_REVISIONS)))
1139   parser.add_remote_option('--profile', default=None, type='string',
1140                            action='store', dest='profile',
1141                            help='Name of profile to sub-specify board variant.')
1142
1143   #
1144   # Patch selection options.
1145   #
1146
1147   group = CustomGroup(
1148       parser,
1149       'Patch Options')
1150
1151   group.add_remote_option('-g', '--gerrit-patches', action='extend',
1152                           default=[], type='string',
1153                           metavar="'Id1 *int_Id2...IdN'",
1154                           help=('Space-separated list of short-form Gerrit '
1155                                 "Change-Id's or change numbers to patch. "
1156                                 "Please prepend '*' to internal Change-Id's"))
1157   group.add_remote_option('-G', '--rietveld-patches', action='extend',
1158                           default=[], type='string',
1159                           metavar="'id1[:subdir1]...idN[:subdirN]'",
1160                           help=('Space-separated list of short-form Rietveld '
1161                                 'issue numbers to patch. If no subdir is '
1162                                 'specified, the src directory is used.'))
1163   group.add_option('-p', '--local-patches', action='extend', default=[],
1164                    metavar="'<project1>[:<branch1>]...<projectN>[:<branchN>]'",
1165                    help=('Space-separated list of project branches with '
1166                          'patches to apply.  Projects are specified by name. '
1167                          'If no branch is specified the current branch of the '
1168                          'project will be used.'))
1169
1170   parser.add_option_group(group)
1171
1172   #
1173   # Remote trybot options.
1174   #
1175
1176   group = CustomGroup(
1177       parser,
1178       'Remote Trybot Options (--remote)')
1179
1180   group.add_remote_option('--hwtest', dest='hwtest', action='store_true',
1181                           default=False,
1182                           help='Run the HWTest stage (tests on real hardware)')
1183   group.add_option('--remote-description', default=None,
1184                    help=('Attach an optional description to a --remote run '
1185                          'to make it easier to identify the results when it '
1186                          'finishes'))
1187   group.add_option('--slaves', action='extend', default=[],
1188                    help=('Specify specific remote tryslaves to run on (e.g. '
1189                          'build149-m2); if the bot is busy, it will be queued'))
1190   group.add_remote_option('--channel', dest='channels', action='extend',
1191                           default=[],
1192                           help=('Specify a channel for a payloads trybot. Can '
1193                                 'be specified multiple times. No valid for '
1194                                 'non-payloads configs.'))
1195   group.add_option('--test-tryjob', action='store_true',
1196                    default=False,
1197                    help=('Submit a tryjob to the test repository.  Will not '
1198                          'show up on the production trybot waterfall.'))
1199
1200   parser.add_option_group(group)
1201
1202   #
1203   # Branch creation options.
1204   #
1205
1206   group = CustomGroup(
1207       parser,
1208       'Branch Creation Options (used with branch-util)')
1209
1210   group.add_remote_option('--branch-name',
1211                           help='The branch to create or delete.')
1212   group.add_remote_option('--delete-branch', default=False, action='store_true',
1213                           help='Delete the branch specified in --branch-name.')
1214   group.add_remote_option('--rename-to', type='string',
1215                           help='Rename a branch to the specified name.')
1216   group.add_remote_option('--force-create', default=False, action='store_true',
1217                           help='Overwrites an existing branch.')
1218
1219   parser.add_option_group(group)
1220
1221   #
1222   # Advanced options.
1223   #
1224
1225   group = CustomGroup(
1226       parser,
1227       'Advanced Options',
1228       'Caution: use these options at your own risk.')
1229
1230   group.add_remote_option('--bootstrap-args', action='append', default=[],
1231                           help=('Args passed directly to the bootstrap re-exec '
1232                                 'to skip verification by the bootstrap code'))
1233   group.add_remote_option('--buildbot', dest='buildbot', action='store_true',
1234                           default=False, help='This is running on a buildbot')
1235   group.add_remote_option('--buildnumber', help='build number', type='int',
1236                           default=0)
1237   group.add_option('--chrome_root', default=None, type='path',
1238                    action='callback', callback=_CheckChromeRootOption,
1239                    dest='chrome_root', help='Local checkout of Chrome to use.')
1240   group.add_remote_option('--chrome_version', default=None, type='string',
1241                           action='callback', dest='chrome_version',
1242                           callback=_CheckChromeVersionOption,
1243                           help=('Used with SPEC logic to force a particular '
1244                                 'git revision of chrome rather than the '
1245                                 'latest.'))
1246   group.add_remote_option('--clobber', action='store_true', dest='clobber',
1247                           default=False,
1248                           help='Clears an old checkout before syncing')
1249   group.add_remote_option('--latest-toolchain', action='store_true',
1250                           default=False,
1251                           help='Use the latest toolchain.')
1252   parser.add_option('--log_dir', dest='log_dir', type='path',
1253                     help=('Directory where logs are stored.'))
1254   group.add_remote_option('--maxarchives', dest='max_archive_builds',
1255                           default=3, type='int',
1256                           help='Change the local saved build count limit.')
1257   parser.add_remote_option('--manifest-repo-url',
1258                            help=('Overrides the default manifest repo url.'))
1259   group.add_remote_option('--compilecheck', action='store_true', default=False,
1260                           help='Only verify compilation and unit tests.')
1261   group.add_remote_option('--noarchive', action='store_false', dest='archive',
1262                           default=True, help="Don't run archive stage.")
1263   group.add_remote_option('--nobootstrap', action='store_false',
1264                           dest='bootstrap', default=True,
1265                           help=("Don't checkout and run from a standalone "
1266                                 'chromite repo.'))
1267   group.add_remote_option('--nobuild', action='store_false', dest='build',
1268                           default=True,
1269                           help="Don't actually build (for cbuildbot dev)")
1270   group.add_remote_option('--noclean', action='store_false', dest='clean',
1271                           default=True, help="Don't clean the buildroot")
1272   group.add_remote_option('--nocgroups', action='store_false', dest='cgroups',
1273                           default=True,
1274                           help='Disable cbuildbots usage of cgroups.')
1275   group.add_remote_option('--nochromesdk', action='store_false',
1276                           dest='chrome_sdk', default=True,
1277                           help=("Don't run the ChromeSDK stage which builds "
1278                                 'Chrome outside of the chroot.'))
1279   group.add_remote_option('--noprebuilts', action='store_false',
1280                           dest='prebuilts', default=True,
1281                           help="Don't upload prebuilts.")
1282   group.add_remote_option('--nopatch', action='store_false',
1283                           dest='postsync_patch', default=True,
1284                           help=("Don't run PatchChanges stage.  This does not "
1285                                 'disable patching in of chromite patches '
1286                                 'during BootstrapStage.'))
1287   group.add_remote_option('--nopaygen', action='store_false',
1288                           dest='paygen', default=True,
1289                           help="Don't generate payloads.")
1290   group.add_remote_option('--noreexec', action='store_false',
1291                           dest='postsync_reexec', default=True,
1292                           help="Don't reexec into the buildroot after syncing.")
1293   group.add_remote_option('--nosdk', action='store_true',
1294                           default=False,
1295                           help='Re-create the SDK from scratch.')
1296   group.add_remote_option('--nosync', action='store_false', dest='sync',
1297                           default=True, help="Don't sync before building.")
1298   group.add_remote_option('--notests', action='store_false', dest='tests',
1299                           default=True,
1300                           help=('Override values from buildconfig and run no '
1301                                 'tests.'))
1302   group.add_remote_option('--noimagetests', action='store_false',
1303                           dest='image_test', default=True,
1304                           help=('Override values from buildconfig and run no '
1305                                 'image tests.'))
1306   group.add_remote_option('--nouprev', action='store_false', dest='uprev',
1307                           default=True,
1308                           help=('Override values from buildconfig and never '
1309                                 'uprev.'))
1310   group.add_option('--reference-repo', action='store', default=None,
1311                    dest='reference_repo',
1312                    help=('Reuse git data stored in an existing repo '
1313                          'checkout. This can drastically reduce the network '
1314                          'time spent setting up the trybot checkout.  By '
1315                          "default, if this option isn't given but cbuildbot "
1316                          'is invoked from a repo checkout, cbuildbot will '
1317                          'use the repo root.'))
1318   group.add_option('--resume', action='store_true', default=False,
1319                    help='Skip stages already successfully completed.')
1320   group.add_remote_option('--timeout', action='store', type='int', default=0,
1321                           help=('Specify the maximum amount of time this job '
1322                                 'can run for, at which point the build will be '
1323                                 'aborted.  If set to zero, then there is no '
1324                                 'timeout.'))
1325   group.add_remote_option('--version', dest='force_version', default=None,
1326                           help=('Used with manifest logic.  Forces use of this '
1327                                 'version rather than create or get latest. '
1328                                 'Examples: 4815.0.0-rc1, 4815.1.2'))
1329
1330   parser.add_option_group(group)
1331
1332   #
1333   # Internal options.
1334   #
1335
1336   group = CustomGroup(
1337       parser,
1338       'Internal Chromium OS Build Team Options',
1339       'Caution: these are for meant for the Chromium OS build team only')
1340
1341   group.add_remote_option('--archive-base', type='gs_path',
1342                           help=('Base GS URL (gs://<bucket_name>/<path>) to '
1343                                 'upload archive artifacts to'))
1344   group.add_remote_option(
1345       '--cq-gerrit-query', dest='cq_gerrit_override', default=None,
1346       help=('If given, this gerrit query will be used to find what patches to '
1347             "test, rather than the normal 'CommitQueue>=1 AND Verified=1 AND "
1348             "CodeReview=2' query it defaults to.  Use with care- note "
1349             'additionally this setting only has an effect if the buildbot '
1350             "target is a cq target, and we're in buildbot mode."))
1351   group.add_option('--pass-through', dest='pass_through_args', action='append',
1352                    type='string', default=[])
1353   group.add_remote_option('--pre-cq', action='store_true', default=False,
1354                           help='Mark CLs as tested by the PreCQ on success.')
1355   group.add_option('--reexec-api-version', dest='output_api_version',
1356                    action='store_true', default=False,
1357                    help=('Used for handling forwards/backwards compatibility '
1358                          'with --resume and --bootstrap'))
1359   group.add_option('--remote-trybot', dest='remote_trybot',
1360                    action='store_true', default=False,
1361                    help='Indicates this is running on a remote trybot machine')
1362   group.add_remote_option('--remote-patches', action='extend', default=[],
1363                           help=('Patches uploaded by the trybot client when '
1364                                 'run using the -p option'))
1365   # Note the default here needs to be hardcoded to 3; that is the last version
1366   # that lacked this functionality.
1367   group.add_option('--remote-version', default=3, type=int, action='store',
1368                    help=('Used for compatibility checks w/tryjobs running in '
1369                          'older chromite instances'))
1370   group.add_option('--sourceroot', type='path', default=constants.SOURCE_ROOT)
1371   group.add_remote_option('--test-bootstrap', action='store_true',
1372                           default=False,
1373                           help=('Causes cbuildbot to bootstrap itself twice, '
1374                                 'in the sequence A->B->C: A(unpatched) patches '
1375                                 'and bootstraps B; B patches and bootstraps C'))
1376   group.add_remote_option('--validation_pool', default=None,
1377                           help=('Path to a pickled validation pool. Intended '
1378                                 'for use only with the commit queue.'))
1379   group.add_remote_option('--metadata_dump', default=None,
1380                           help=('Path to a json dumped metadata file. This '
1381                                 'will be used as the initial metadata.'))
1382   group.add_remote_option('--master-build-id', default=None, type=int,
1383                           api=constants.REEXEC_API_MASTER_BUILD_ID,
1384                           help=('cidb build id of the master build to this '
1385                                 'slave build.'))
1386   group.add_remote_option('--mock-tree-status', dest='mock_tree_status',
1387                           default=None, action='store',
1388                           help=('Override the tree status value that would be '
1389                                 'returned from the the actual tree. Example '
1390                                 'values: open, closed, throttled. When used '
1391                                 'in conjunction with --debug, the tree status '
1392                                 'will not be ignored as it usually is in a '
1393                                 '--debug run.'))
1394   group.add_remote_option(
1395       '--mock-slave-status', dest='mock_slave_status', default=None,
1396       action='store', metavar='MOCK_SLAVE_STATUS_PICKLE_FILE',
1397       help=('Override the result of the _FetchSlaveStatuses method of '
1398             'MasterSlaveSyncCompletionStage, by specifying a file with a '
1399             'pickle of the result to be returned.'))
1400
1401   parser.add_option_group(group)
1402
1403   #
1404   # Debug options
1405   #
1406   # Temporary hack; in place till --dry-run replaces --debug.
1407   # pylint: disable=W0212
1408   group = parser.debug_group
1409   debug = [x for x in group.option_list if x._long_opts == ['--debug']][0]
1410   debug.help += '  Currently functions as --dry-run in addition.'
1411   debug.pass_through = True
1412   group.add_option('--notee', action='store_false', dest='tee', default=True,
1413                    help=('Disable logging and internal tee process.  Primarily '
1414                          'used for debugging cbuildbot itself.'))
1415   return parser
1416
1417
1418 def _FinishParsing(options, args):
1419   """Perform some parsing tasks that need to take place after optparse.
1420
1421   This function needs to be easily testable!  Keep it free of
1422   environment-dependent code.  Put more detailed usage validation in
1423   _PostParseCheck().
1424
1425   Args:
1426     options: The options object returned by optparse
1427     args: The args object returned by optparse
1428   """
1429   # Populate options.pass_through_args.
1430   accepted, _ = commandline.FilteringParser.FilterArgs(
1431       options.parsed_args, lambda x: x.opt_inst.pass_through)
1432   options.pass_through_args.extend(accepted)
1433
1434   if options.chrome_root:
1435     if options.chrome_rev != constants.CHROME_REV_LOCAL:
1436       cros_build_lib.Die('Chrome rev must be %s if chrome_root is set.' %
1437                          constants.CHROME_REV_LOCAL)
1438   elif options.chrome_rev == constants.CHROME_REV_LOCAL:
1439     cros_build_lib.Die('Chrome root must be set if chrome_rev is %s.' %
1440                        constants.CHROME_REV_LOCAL)
1441
1442   if options.chrome_version:
1443     if options.chrome_rev != constants.CHROME_REV_SPEC:
1444       cros_build_lib.Die('Chrome rev must be %s if chrome_version is set.' %
1445                          constants.CHROME_REV_SPEC)
1446   elif options.chrome_rev == constants.CHROME_REV_SPEC:
1447     cros_build_lib.Die(
1448         'Chrome rev must not be %s if chrome_version is not set.'
1449         % constants.CHROME_REV_SPEC)
1450
1451   patches = bool(options.gerrit_patches or options.local_patches or
1452                  options.rietveld_patches)
1453   if options.remote:
1454     if options.local:
1455       cros_build_lib.Die('Cannot specify both --remote and --local')
1456
1457     # options.channels is a convenient way to detect payloads builds.
1458     if not options.buildbot and not options.channels and not patches:
1459       prompt = ('No patches were provided; are you sure you want to just '
1460                 'run a remote build of %s?' % (
1461                     options.branch if options.branch else 'ToT'))
1462       if not cros_build_lib.BooleanPrompt(prompt=prompt, default=False):
1463         cros_build_lib.Die('Must provide patches when running with --remote.')
1464
1465     # --debug needs to be explicitly passed through for remote invocations.
1466     release_mode_with_patches = (options.buildbot and patches and
1467                                  '--debug' not in options.pass_through_args)
1468   else:
1469     if len(args) > 1:
1470       cros_build_lib.Die('Multiple configs not supported if not running with '
1471                          '--remote.  Got %r', args)
1472
1473     if options.slaves:
1474       cros_build_lib.Die('Cannot use --slaves if not running with --remote.')
1475
1476     release_mode_with_patches = (options.buildbot and patches and
1477                                  not options.debug)
1478
1479   # When running in release mode, make sure we are running with checked-in code.
1480   # We want checked-in cbuildbot/scripts to prevent errors, and we want to build
1481   # a release image with checked-in code for CrOS packages.
1482   if release_mode_with_patches:
1483     cros_build_lib.Die(
1484         'Cannot provide patches when running with --buildbot!')
1485
1486   if options.buildbot and options.remote_trybot:
1487     cros_build_lib.Die(
1488         '--buildbot and --remote-trybot cannot be used together.')
1489
1490   # Record whether --debug was set explicitly vs. it was inferred.
1491   options.debug_forced = False
1492   if options.debug:
1493     options.debug_forced = True
1494   if not options.debug:
1495     # We don't set debug by default for
1496     # 1. --buildbot invocations.
1497     # 2. --remote invocations, because it needs to push changes to the tryjob
1498     #    repo.
1499     options.debug = not options.buildbot and not options.remote
1500
1501   # Record the configs targeted.
1502   options.build_targets = args[:]
1503
1504   if constants.BRANCH_UTIL_CONFIG in options.build_targets:
1505     if options.remote:
1506       cros_build_lib.Die(
1507           'Running %s as a remote tryjob is not yet supported.',
1508           constants.BRANCH_UTIL_CONFIG)
1509     if len(options.build_targets) > 1:
1510       cros_build_lib.Die(
1511           'Cannot run %s with any other configs.',
1512           constants.BRANCH_UTIL_CONFIG)
1513     if not options.branch_name:
1514       cros_build_lib.Die(
1515           'Must specify --branch-name with the %s config.',
1516           constants.BRANCH_UTIL_CONFIG)
1517     if options.branch and options.branch != options.branch_name:
1518       cros_build_lib.Die(
1519           'If --branch is specified with the %s config, it must'
1520           ' have the same value as --branch-name.',
1521           constants.BRANCH_UTIL_CONFIG)
1522
1523     exclusive_opts = {'--version': options.force_version,
1524                       '--delete-branch': options.delete_branch,
1525                       '--rename-to': options.rename_to}
1526     if 1 != sum(1 for x in exclusive_opts.values() if x):
1527       cros_build_lib.Die('When using the %s config, you must'
1528                          ' specifiy one and only one of the following'
1529                          ' options: %s.', constants.BRANCH_UTIL_CONFIG,
1530                          ', '.join(exclusive_opts.keys()))
1531
1532     # When deleting or renaming a branch, the --branch and --nobootstrap
1533     # options are implied.
1534     if options.delete_branch or options.rename_to:
1535       if not options.branch:
1536         cros_build_lib.Info('Automatically enabling sync to branch %s'
1537                             ' for this %s flow.', options.branch_name,
1538                             constants.BRANCH_UTIL_CONFIG)
1539         options.branch = options.branch_name
1540       if options.bootstrap:
1541         cros_build_lib.Info('Automatically disabling bootstrap step for'
1542                             ' this %s flow.', constants.BRANCH_UTIL_CONFIG)
1543         options.bootstrap = False
1544
1545   elif any([options.delete_branch, options.rename_to, options.branch_name]):
1546     cros_build_lib.Die(
1547         'Cannot specify --delete-branch, --rename-to or --branch-name when not '
1548         'running the %s config', constants.BRANCH_UTIL_CONFIG)
1549
1550
1551 # pylint: disable=W0613
1552 def _PostParseCheck(parser, options, args):
1553   """Perform some usage validation after we've parsed the arguments
1554
1555   Args:
1556     parser: Option parser that was used to parse arguments.
1557     options: The options returned by optparse.
1558     args: The args returned by optparse.
1559   """
1560   if not options.branch:
1561     options.branch = git.GetChromiteTrackingBranch()
1562
1563   if not repository.IsARepoRoot(options.sourceroot):
1564     if options.local_patches:
1565       raise Exception('Could not find repo checkout at %s!'
1566                       % options.sourceroot)
1567
1568   # Because the default cache dir depends on other options, FindCacheDir
1569   # always returns None, and we setup the default here.
1570   if options.cache_dir is None:
1571     # Note, options.sourceroot is set regardless of the path
1572     # actually existing.
1573     if options.buildroot is not None:
1574       options.cache_dir = os.path.join(options.buildroot, '.cache')
1575     elif os.path.exists(options.sourceroot):
1576       options.cache_dir = os.path.join(options.sourceroot, '.cache')
1577     else:
1578       options.cache_dir = parser.FindCacheDir(parser, options)
1579     options.cache_dir = os.path.abspath(options.cache_dir)
1580     parser.ConfigureCacheDir(options.cache_dir)
1581
1582   osutils.SafeMakedirsNonRoot(options.cache_dir)
1583
1584   if options.local_patches:
1585     options.local_patches = _CheckLocalPatches(
1586         options.sourceroot, options.local_patches)
1587
1588   default = os.environ.get('CBUILDBOT_DEFAULT_MODE')
1589   if (default and not any([options.local, options.buildbot,
1590                            options.remote, options.remote_trybot])):
1591     cros_build_lib.Info('CBUILDBOT_DEFAULT_MODE=%s env var detected, using it.'
1592                         % default)
1593     default = default.lower()
1594     if default == 'local':
1595       options.local = True
1596     elif default == 'remote':
1597       options.remote = True
1598     elif default == 'buildbot':
1599       options.buildbot = True
1600     else:
1601       cros_build_lib.Die("CBUILDBOT_DEFAULT_MODE value %s isn't supported. "
1602                          % default)
1603
1604   # Ensure that all args are legitimate config targets.
1605   invalid_targets = []
1606   for arg in args:
1607     build_config = _GetConfig(arg)
1608
1609     if not build_config:
1610       invalid_targets.append(arg)
1611       cros_build_lib.Error('No such configuraton target: "%s".', arg)
1612       continue
1613
1614     is_payloads_build = build_config.build_type == constants.PAYLOADS_TYPE
1615
1616     if options.channels and not is_payloads_build:
1617       cros_build_lib.Die('--channel must only be used with a payload config,'
1618                          ' not target (%s).' % arg)
1619
1620     if not options.channels and is_payloads_build:
1621       cros_build_lib.Die('payload configs (%s) require --channel to do anything'
1622                          ' useful.' % arg)
1623
1624     # The --version option is not compatible with an external target unless the
1625     # --buildbot option is specified.  More correctly, only "paladin versions"
1626     # will work with external targets, and those are only used with --buildbot.
1627     # If --buildbot is specified, then user should know what they are doing and
1628     # only specify a version that will work.  See crbug.com/311648.
1629     if (options.force_version and
1630         not (options.buildbot or build_config.internal)):
1631       cros_build_lib.Die('Cannot specify --version without --buildbot for an'
1632                          ' external target (%s).' % arg)
1633
1634   if invalid_targets:
1635     cros_build_lib.Die('One or more invalid configuration targets specified. '
1636                        'You can check the available configs by running '
1637                        '`cbuildbot --list --all`')
1638
1639
1640 def _ParseCommandLine(parser, argv):
1641   """Completely parse the commandline arguments"""
1642   (options, args) = parser.parse_args(argv)
1643
1644   # Strip out null arguments.
1645   # TODO(rcui): Remove when buildbot is fixed
1646   args = [arg for arg in args if arg]
1647
1648   # A couple options, like --list, trigger a quick exit.
1649   if options.output_api_version:
1650     print(constants.REEXEC_API_VERSION)
1651     sys.exit(0)
1652
1653   if options.list:
1654     if args:
1655       cros_build_lib.Die('No arguments expected with the --list options.')
1656     _PrintValidConfigs(options.print_all)
1657     sys.exit(0)
1658
1659   if not args:
1660     parser.error('Invalid usage: no configuration targets provided.'
1661                  'Use -h to see usage.  Use -l to list supported configs.')
1662
1663   _FinishParsing(options, args)
1664   return options, args
1665
1666
1667 def _SetupCidb(options, build_config):
1668   """Set up CIDB the appropriate Setup call.
1669
1670   Args:
1671     options: Command line options structure.
1672     build_config: Config object for this build.
1673   """
1674   # TODO(akeshet): This is a temporary workaround to make sure that the cidb
1675   # is not used on waterfalls that the db schema does not support (in particular
1676   # the chromeos.chrome waterfall).
1677   # See crbug.com/406940
1678   waterfall = os.environ.get('BUILDBOT_MASTERNAME', '')
1679   if not waterfall in constants.CIDB_KNOWN_WATERFALLS:
1680     cidb.CIDBConnectionFactory.SetupNoCidb()
1681     return
1682
1683   # TODO(akeshet): Clean up this code once we have better defined flags to
1684   # specify on-or-off waterfall and on-or-off production runs of cbuildbot.
1685   # See crbug.com/331417
1686
1687   # --buildbot runs should use the production database, unless the --debug flag
1688   # is also present in which case they should use the debug database.
1689   if options.buildbot:
1690     if options.debug:
1691       cidb.CIDBConnectionFactory.SetupDebugCidb()
1692       return
1693     else:
1694       cidb.CIDBConnectionFactory.SetupProdCidb()
1695       return
1696
1697   # --remote-trybot runs should use the debug database. With the exception of
1698   # pre-cq builds, which should use the production database.
1699   if options.remote_trybot:
1700     if build_config['pre_cq']:
1701       cidb.CIDBConnectionFactory.SetupProdCidb()
1702       return
1703     else:
1704       cidb.CIDBConnectionFactory.SetupDebugCidb()
1705       return
1706
1707   # If neither --buildbot nor --remote-trybot flag was used, don't use the
1708   # database.
1709   cidb.CIDBConnectionFactory.SetupNoCidb()
1710
1711
1712 # TODO(build): This function is too damn long.
1713 def main(argv):
1714   # Turn on strict sudo checks.
1715   cros_build_lib.STRICT_SUDO = True
1716
1717   # Set umask to 022 so files created by buildbot are readable.
1718   os.umask(0o22)
1719
1720   parser = _CreateParser()
1721   (options, args) = _ParseCommandLine(parser, argv)
1722
1723   _PostParseCheck(parser, options, args)
1724
1725   cros_build_lib.AssertOutsideChroot()
1726
1727   if options.remote:
1728     cros_build_lib.logger.setLevel(logging.WARNING)
1729
1730     # Verify configs are valid.
1731     # If hwtest flag is enabled, post a warning that HWTest step may fail if the
1732     # specified board is not a released platform or it is a generic overlay.
1733     for bot in args:
1734       build_config = _GetConfig(bot)
1735       if options.hwtest:
1736         cros_build_lib.Warning(
1737             'If %s is not a released platform or it is a generic overlay, '
1738             'the HWTest step will most likely not run; please ask the lab '
1739             'team for help if this is unexpected.' % build_config['boards'])
1740
1741     # Verify gerrit patches are valid.
1742     print('Verifying patches...')
1743     patch_pool = AcquirePoolFromOptions(options)
1744
1745     # --debug need to be explicitly passed through for remote invocations.
1746     if options.buildbot and '--debug' not in options.pass_through_args:
1747       _ConfirmRemoteBuildbotRun()
1748
1749     print('Submitting tryjob...')
1750     tryjob = remote_try.RemoteTryJob(options, args, patch_pool.local_patches)
1751     tryjob.Submit(testjob=options.test_tryjob, dryrun=False)
1752     print('Tryjob submitted!')
1753     print(('Go to %s to view the status of your job.'
1754            % tryjob.GetTrybotWaterfallLink()))
1755     sys.exit(0)
1756
1757   elif (not options.buildbot and not options.remote_trybot
1758         and not options.resume and not options.local):
1759     cros_build_lib.Die('Please use --remote or --local to run trybots')
1760
1761   # Only one config arg is allowed in this mode, which was confirmed earlier.
1762   bot_id = args[-1]
1763   build_config = _GetConfig(bot_id)
1764
1765   # TODO: Re-enable this block when reference_repo support handles this
1766   #       properly. (see chromium:330775)
1767   # if options.reference_repo is None:
1768   #   repo_path = os.path.join(options.sourceroot, '.repo')
1769   #   # If we're being run from a repo checkout, reuse the repo's git pool to
1770   #   # cut down on sync time.
1771   #   if os.path.exists(repo_path):
1772   #     options.reference_repo = options.sourceroot
1773
1774   if options.reference_repo:
1775     if not os.path.exists(options.reference_repo):
1776       parser.error('Reference path %s does not exist'
1777                    % (options.reference_repo,))
1778     elif not os.path.exists(os.path.join(options.reference_repo, '.repo')):
1779       parser.error('Reference path %s does not look to be the base of a '
1780                    'repo checkout; no .repo exists in the root.'
1781                    % (options.reference_repo,))
1782
1783   if (options.buildbot or options.remote_trybot) and not options.resume:
1784     if not options.cgroups:
1785       parser.error('Options --buildbot/--remote-trybot and --nocgroups cannot '
1786                    'be used together.  Cgroup support is required for '
1787                    'buildbot/remote-trybot mode.')
1788     if not cgroups.Cgroup.IsSupported():
1789       parser.error('Option --buildbot/--remote-trybot was given, but this '
1790                    'system does not support cgroups.  Failing.')
1791
1792     missing = osutils.FindMissingBinaries(_BUILDBOT_REQUIRED_BINARIES)
1793     if missing:
1794       parser.error('Option --buildbot/--remote-trybot requires the following '
1795                    "binaries which couldn't be found in $PATH: %s"
1796                    % (', '.join(missing)))
1797
1798   if options.reference_repo:
1799     options.reference_repo = os.path.abspath(options.reference_repo)
1800
1801   if not options.buildroot:
1802     if options.buildbot:
1803       parser.error('Please specify a buildroot with the --buildbot option.')
1804
1805     options.buildroot = _DetermineDefaultBuildRoot(options.sourceroot,
1806                                                    build_config['internal'])
1807     # We use a marker file in the buildroot to indicate the user has
1808     # consented to using this directory.
1809     if not os.path.exists(repository.GetTrybotMarkerPath(options.buildroot)):
1810       _ConfirmBuildRoot(options.buildroot)
1811
1812   # Sanity check of buildroot- specifically that it's not pointing into the
1813   # midst of an existing repo since git-repo doesn't support nesting.
1814   if (not repository.IsARepoRoot(options.buildroot) and
1815       git.FindRepoDir(options.buildroot)):
1816     parser.error('Configured buildroot %s points into a repository checkout, '
1817                  'rather than the root of it.  This is not supported.'
1818                  % options.buildroot)
1819
1820   if not options.log_dir:
1821     options.log_dir = os.path.join(options.buildroot, _DEFAULT_LOG_DIR)
1822
1823   log_file = None
1824   if options.tee:
1825     log_file = os.path.join(options.log_dir, _BUILDBOT_LOG_FILE)
1826     osutils.SafeMakedirs(options.log_dir)
1827     _BackupPreviousLog(log_file)
1828
1829   with cros_build_lib.ContextManagerStack() as stack:
1830     # TODO(ferringb): update this once
1831     # https://chromium-review.googlesource.com/25359
1832     # is landed- it's sensitive to the manifest-versions cache path.
1833     options.preserve_paths = set(['manifest-versions', '.cache',
1834                                   'manifest-versions-internal'])
1835     if log_file is not None:
1836       # We don't want the critical section to try to clean up the tee process,
1837       # so we run Tee (forked off) outside of it. This prevents a deadlock
1838       # because the Tee process only exits when its pipe is closed, and the
1839       # critical section accidentally holds on to that file handle.
1840       stack.Add(tee.Tee, log_file)
1841       options.preserve_paths.add(_DEFAULT_LOG_DIR)
1842
1843     critical_section = stack.Add(cleanup.EnforcedCleanupSection)
1844     stack.Add(sudo.SudoKeepAlive)
1845
1846     if not options.resume:
1847       # If we're in resume mode, use our parents tempdir rather than
1848       # nesting another layer.
1849       stack.Add(osutils.TempDir, prefix='cbuildbot-tmp', set_global=True)
1850       logging.debug('Cbuildbot tempdir is %r.', os.environ.get('TMP'))
1851
1852     if options.cgroups:
1853       stack.Add(cgroups.SimpleContainChildren, 'cbuildbot')
1854
1855     # Mark everything between EnforcedCleanupSection and here as having to
1856     # be rolled back via the contextmanager cleanup handlers.  This
1857     # ensures that sudo bits cannot outlive cbuildbot, that anything
1858     # cgroups would kill gets killed, etc.
1859     stack.Add(critical_section.ForkWatchdog)
1860
1861     if options.timeout > 0:
1862       stack.Add(timeout_util.FatalTimeout, options.timeout)
1863
1864     if not options.buildbot:
1865       build_config = cbuildbot_config.OverrideConfigForTrybot(
1866           build_config, options)
1867
1868     if options.mock_tree_status is not None:
1869       stack.Add(mock.patch.object, timeout_util, '_GetStatus',
1870                 return_value=options.mock_tree_status)
1871
1872     if options.mock_slave_status is not None:
1873       with open(options.mock_slave_status, 'r') as f:
1874         mock_statuses = pickle.load(f)
1875         for key, value in mock_statuses.iteritems():
1876           mock_statuses[key] = manifest_version.BuilderStatus(**value)
1877       stack.Add(mock.patch.object,
1878                 completion_stages.MasterSlaveSyncCompletionStage,
1879                 '_FetchSlaveStatuses',
1880                 return_value=mock_statuses)
1881
1882     _SetupCidb(options, build_config)
1883
1884     _RunBuildStagesWrapper(options, build_config)