1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
|
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module containing the simple builders."""
from __future__ import print_function
import collections
from chromite.cbuildbot import afdo
from chromite.cbuildbot import config_lib
from chromite.cbuildbot import constants
from chromite.cbuildbot import manifest_version
from chromite.cbuildbot import results_lib
from chromite.cbuildbot.builders import generic_builders
from chromite.cbuildbot.stages import afdo_stages
from chromite.cbuildbot.stages import artifact_stages
from chromite.cbuildbot.stages import build_stages
from chromite.cbuildbot.stages import chrome_stages
from chromite.cbuildbot.stages import completion_stages
from chromite.cbuildbot.stages import generic_stages
from chromite.cbuildbot.stages import release_stages
from chromite.cbuildbot.stages import report_stages
from chromite.cbuildbot.stages import sync_stages
from chromite.cbuildbot.stages import test_stages
from chromite.lib import cros_logging as logging
from chromite.lib import patch as cros_patch
from chromite.lib import parallel
# TODO: SimpleBuilder needs to be broken up big time.
BoardConfig = collections.namedtuple('BoardConfig', ['board', 'name'])
class SimpleBuilder(generic_builders.Builder):
"""Builder that performs basic vetting operations."""
def GetSyncInstance(self):
"""Sync to lkgm or TOT as necessary.
Returns:
The instance of the sync stage to run.
"""
if self._run.options.force_version:
sync_stage = self._GetStageInstance(
sync_stages.ManifestVersionedSyncStage)
elif self._run.config.use_lkgm:
sync_stage = self._GetStageInstance(sync_stages.LKGMSyncStage)
elif self._run.config.use_chrome_lkgm:
sync_stage = self._GetStageInstance(chrome_stages.ChromeLKGMSyncStage)
else:
sync_stage = self._GetStageInstance(sync_stages.SyncStage)
return sync_stage
def GetVersionInfo(self):
"""Returns the CrOS version info from the chromiumos-overlay."""
return manifest_version.VersionInfo.from_repo(self._run.buildroot)
def _GetChangesUnderTest(self):
"""Returns the list of GerritPatch changes under test."""
changes = set()
changes_json_list = self._run.attrs.metadata.GetDict().get('changes', [])
for change_dict in changes_json_list:
change = cros_patch.GerritFetchOnlyPatch.FromAttrDict(change_dict)
changes.add(change)
# Also add the changes from PatchChangeStage, the PatchChangeStage doesn't
# write changes into metadata.
if self._run.ShouldPatchAfterSync():
changes.update(set(self.patch_pool.gerrit_patches))
return list(changes)
def _RunHWTests(self, builder_run, board):
"""Run hwtest-related stages for the specified board.
Args:
builder_run: BuilderRun object for these background stages.
board: Board name.
"""
parallel_stages = []
# We can not run hw tests without archiving the payloads.
if builder_run.options.archive:
for suite_config in builder_run.config.hw_tests:
stage_class = None
if suite_config.async:
stage_class = test_stages.ASyncHWTestStage
elif suite_config.suite == constants.HWTEST_AU_SUITE:
stage_class = test_stages.AUTestStage
else:
stage_class = test_stages.HWTestStage
if suite_config.blocking:
self._RunStage(stage_class, board, suite_config,
builder_run=builder_run)
else:
new_stage = self._GetStageInstance(stage_class, board,
suite_config,
builder_run=builder_run)
parallel_stages.append(new_stage)
self._RunParallelStages(parallel_stages)
def _RunBackgroundStagesForBoardAndMarkAsSuccessful(self, builder_run, board):
"""Run background board-specific stages for the specified board.
After finishing the build, mark it as successful.
Args:
builder_run: BuilderRun object for these background stages.
board: Board name.
"""
self._RunBackgroundStagesForBoard(builder_run, board)
board_runattrs = builder_run.GetBoardRunAttrs(board)
board_runattrs.SetParallel('success', True)
def _RunBackgroundStagesForBoard(self, builder_run, board):
"""Run background board-specific stages for the specified board.
Used by _RunBackgroundStagesForBoardAndMarkAsSuccessful. Callers should use
that method instead.
Args:
builder_run: BuilderRun object for these background stages.
board: Board name.
"""
config = builder_run.config
# TODO(mtennant): This is the last usage of self.archive_stages. We can
# kill it once we migrate its uses to BuilderRun so that none of the
# stages below need it as an argument.
archive_stage = self.archive_stages[BoardConfig(board, config.name)]
if config.afdo_generate_min:
self._RunParallelStages([archive_stage])
return
# paygen can't complete without push_image.
assert not config.paygen or config.push_image
if config.build_packages_in_background:
self._RunStage(build_stages.BuildPackagesStage, board,
update_metadata=True, builder_run=builder_run,
afdo_use=config.afdo_use)
if builder_run.config.compilecheck or builder_run.options.compilecheck:
self._RunStage(test_stages.UnitTestStage, board,
builder_run=builder_run)
return
# Build the image first before doing anything else.
# TODO(davidjames): Remove this lock once http://crbug.com/352994 is fixed.
with self._build_image_lock:
self._RunStage(build_stages.BuildImageStage, board,
builder_run=builder_run, afdo_use=config.afdo_use)
# While this stage list is run in parallel, the order here dictates the
# order that things will be shown in the log. So group things together
# that make sense when read in order. Also keep in mind that, since we
# gather output manually, early slow stages will prevent any output from
# later stages showing up until it finishes.
# Determine whether to run the DetectIrrelevantChangesStage
stage_list = []
changes = self._GetChangesUnderTest()
if changes:
stage_list += [[report_stages.DetectIrrelevantChangesStage, board,
changes]]
stage_list += [[chrome_stages.ChromeSDKStage, board]]
if config.vm_test_runs > 1:
# Run the VMTests multiple times to see if they fail.
stage_list += [
[generic_stages.RepeatStage, config.vm_test_runs,
test_stages.VMTestStage, board]]
else:
# Give the VMTests one retry attempt in case failures are flaky.
stage_list += [[generic_stages.RetryStage, 1, test_stages.VMTestStage,
board]]
if config.afdo_generate:
stage_list += [[afdo_stages.AFDODataGenerateStage, board]]
stage_list += [
[release_stages.SignerTestStage, board, archive_stage],
[release_stages.PaygenStage, board, archive_stage],
[test_stages.ImageTestStage, board],
[test_stages.UnitTestStage, board],
[artifact_stages.UploadPrebuiltsStage, board],
[artifact_stages.DevInstallerPrebuiltsStage, board],
[artifact_stages.DebugSymbolsStage, board],
[artifact_stages.CPEExportStage, board],
[artifact_stages.UploadTestArtifactsStage, board],
]
stage_objs = [self._GetStageInstance(*x, builder_run=builder_run)
for x in stage_list]
parallel.RunParallelSteps([
lambda: self._RunParallelStages(stage_objs + [archive_stage]),
lambda: self._RunHWTests(builder_run, board),
])
def RunSetupBoard(self):
"""Run the SetupBoard stage for all child configs and boards."""
for builder_run in self._run.GetUngroupedBuilderRuns():
for board in builder_run.config.boards:
self._RunStage(build_stages.SetupBoardStage, board,
builder_run=builder_run)
def _RunMasterPaladinOrChromePFQBuild(self):
"""Runs through the stages of the paladin or chrome PFQ master build."""
self._RunStage(build_stages.UprevStage)
self._RunStage(build_stages.InitSDKStage)
# The CQ/Chrome PFQ master will not actually run the SyncChrome stage, but
# we want the logic that gets triggered when SyncChrome stage is skipped.
self._RunStage(chrome_stages.SyncChromeStage)
if self._run.config.build_type == constants.PALADIN_TYPE:
self._RunStage(build_stages.RegenPortageCacheStage)
self._RunStage(test_stages.BinhostTestStage)
self._RunStage(test_stages.BranchUtilTestStage)
self._RunStage(artifact_stages.MasterUploadPrebuiltsStage)
def _RunDefaultTypeBuild(self):
"""Runs through the stages of a non-special-type build."""
self._RunStage(build_stages.UprevStage)
self._RunStage(build_stages.InitSDKStage)
self._RunStage(build_stages.RegenPortageCacheStage)
self.RunSetupBoard()
self._RunStage(chrome_stages.SyncChromeStage)
self._RunStage(chrome_stages.PatchChromeStage)
self._RunStage(test_stages.BinhostTestStage)
self._RunStage(test_stages.BranchUtilTestStage)
# Prepare stages to run in background. If child_configs exist then
# run each of those here, otherwise use default config.
builder_runs = self._run.GetUngroupedBuilderRuns()
tasks = []
for builder_run in builder_runs:
# Prepare a local archive directory for each "run".
builder_run.GetArchive().SetupArchivePath()
for board in builder_run.config.boards:
archive_stage = self._GetStageInstance(
artifact_stages.ArchiveStage, board, builder_run=builder_run,
chrome_version=self._run.attrs.chrome_version)
board_config = BoardConfig(board, builder_run.config.name)
self.archive_stages[board_config] = archive_stage
tasks.append((builder_run, board))
# Set up a process pool to run test/archive stages in the background.
# This process runs task(board) for each board added to the queue.
task_runner = self._RunBackgroundStagesForBoardAndMarkAsSuccessful
with parallel.BackgroundTaskRunner(task_runner) as queue:
for builder_run, board in tasks:
if not builder_run.config.build_packages_in_background:
# Run BuildPackages in the foreground, generating or using AFDO data
# if requested.
kwargs = {'builder_run': builder_run}
if builder_run.config.afdo_generate_min:
kwargs['afdo_generate_min'] = True
elif builder_run.config.afdo_use:
kwargs['afdo_use'] = True
self._RunStage(build_stages.BuildPackagesStage, board,
update_metadata=True, **kwargs)
if (builder_run.config.afdo_generate_min and
afdo.CanGenerateAFDOData(board)):
# Generate the AFDO data before allowing any other tasks to run.
self._RunStage(build_stages.BuildImageStage, board, **kwargs)
self._RunStage(artifact_stages.UploadTestArtifactsStage, board,
builder_run=builder_run,
suffix='[afdo_generate_min]')
for suite in builder_run.config.hw_tests:
self._RunStage(test_stages.HWTestStage, board, suite,
builder_run=builder_run)
self._RunStage(afdo_stages.AFDODataGenerateStage, board,
builder_run=builder_run)
if (builder_run.config.afdo_generate_min and
builder_run.config.afdo_update_ebuild):
self._RunStage(afdo_stages.AFDOUpdateEbuildStage,
builder_run=builder_run)
# Kick off our background stages.
queue.put([builder_run, board])
def RunStages(self):
"""Runs through build process."""
# TODO(sosa): Split these out into classes.
if self._run.config.build_type == constants.PRE_CQ_LAUNCHER_TYPE:
self._RunStage(sync_stages.PreCQLauncherStage)
elif ((self._run.config.build_type == constants.PALADIN_TYPE or
self._run.config.build_type == constants.CHROME_PFQ_TYPE) and
self._run.config.master):
self._RunMasterPaladinOrChromePFQBuild()
else:
self._RunDefaultTypeBuild()
class DistributedBuilder(SimpleBuilder):
"""Build class that has special logic to handle distributed builds.
These builds sync using git/manifest logic in manifest_versions. In general
they use a non-distributed builder code for the bulk of the work.
"""
def __init__(self, *args, **kwargs):
"""Initializes a buildbot builder.
Extra variables:
completion_stage_class: Stage used to complete a build. Set in the Sync
stage.
"""
super(DistributedBuilder, self).__init__(*args, **kwargs)
self.completion_stage_class = None
self.sync_stage = None
self._completion_stage = None
def GetSyncInstance(self):
"""Syncs the tree using one of the distributed sync logic paths.
Returns:
The instance of the sync stage to run.
"""
# Determine sync class to use. CQ overrides PFQ bits so should check it
# first.
if self._run.config.pre_cq:
sync_stage = self._GetStageInstance(sync_stages.PreCQSyncStage,
self.patch_pool.gerrit_patches)
self.completion_stage_class = completion_stages.PreCQCompletionStage
self.patch_pool.gerrit_patches = []
elif config_lib.IsCQType(self._run.config.build_type):
if self._run.config.do_not_apply_cq_patches:
sync_stage = self._GetStageInstance(
sync_stages.MasterSlaveLKGMSyncStage)
else:
sync_stage = self._GetStageInstance(sync_stages.CommitQueueSyncStage)
self.completion_stage_class = completion_stages.CommitQueueCompletionStage
elif config_lib.IsPFQType(self._run.config.build_type):
sync_stage = self._GetStageInstance(sync_stages.MasterSlaveLKGMSyncStage)
self.completion_stage_class = (
completion_stages.MasterSlaveSyncCompletionStage)
elif config_lib.IsCanaryType(self._run.config.build_type):
sync_stage = self._GetStageInstance(
sync_stages.ManifestVersionedSyncStage)
self.completion_stage_class = (
completion_stages.CanaryCompletionStage)
else:
sync_stage = self._GetStageInstance(
sync_stages.ManifestVersionedSyncStage)
self.completion_stage_class = (
completion_stages.ManifestVersionedSyncCompletionStage)
self.sync_stage = sync_stage
return self.sync_stage
def GetCompletionInstance(self):
"""Returns the completion_stage_class instance that was used for this build.
Returns:
None if the completion_stage instance was not yet created (this
occurs during Publish).
"""
return self._completion_stage
def Publish(self, was_build_successful, build_finished):
"""Completes build by publishing any required information.
Args:
was_build_successful: Whether the build succeeded.
build_finished: Whether the build completed. A build can be successful
without completing if it exits early with sys.exit(0).
"""
completion_stage = self._GetStageInstance(self.completion_stage_class,
self.sync_stage,
was_build_successful)
self._completion_stage = completion_stage
completion_successful = False
try:
completion_stage.Run()
completion_successful = True
if (self._run.config.afdo_update_ebuild and
not self._run.config.afdo_generate_min):
self._RunStage(afdo_stages.AFDOUpdateEbuildStage)
finally:
if self._run.config.master:
self._RunStage(report_stages.SlaveFailureSummaryStage)
if self._run.config.push_overlays:
publish = (was_build_successful and completion_successful and
build_finished)
self._RunStage(completion_stages.PublishUprevChangesStage, publish)
def RunStages(self):
"""Runs simple builder logic and publishes information to overlays."""
was_build_successful = False
build_finished = False
try:
super(DistributedBuilder, self).RunStages()
was_build_successful = results_lib.Results.BuildSucceededSoFar()
build_finished = True
except SystemExit as ex:
# If a stage calls sys.exit(0), it's exiting with success, so that means
# we should mark ourselves as successful.
logging.info('Detected sys.exit(%s)', ex.code)
if ex.code == 0:
was_build_successful = True
raise
finally:
self.Publish(was_build_successful, build_finished)
|