OLD | NEW |
| (Empty) |
1 # Copyright (c) 2013 The Chromium 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 | |
6 """Utility class to build the Skia master BuildFactory's. | |
7 | |
8 Based on gclient_factory.py and adds Skia-specific steps.""" | |
9 | |
10 | |
11 from buildbot.process.properties import WithProperties | |
12 from buildbot.status import builder | |
13 from config_private import SKIA_GIT_URL | |
14 from master.factory import gclient_factory | |
15 from master.factory.build_factory import BuildFactory | |
16 from skia_master_scripts import commands as skia_commands | |
17 | |
18 import builder_name_schema | |
19 import config | |
20 import config_private | |
21 import ntpath | |
22 import os | |
23 import posixpath | |
24 import skia_vars | |
25 import utils | |
26 | |
27 | |
28 # TODO(epoger): My intent is to make the build steps identical on all platforms | |
29 # and thus remove the need for the whole target_platform parameter. | |
30 # For now, these must match the target_platform values used in | |
31 # third_party/chromium_buildbot/scripts/master/factory/gclient_factory.py , | |
32 # because we pass these values into GClientFactory.__init__() . | |
33 TARGET_PLATFORM_LINUX = 'linux' | |
34 TARGET_PLATFORM_MAC = 'mac' | |
35 TARGET_PLATFORM_WIN32 = 'win32' | |
36 | |
37 CONFIG_DEBUG = 'Debug' | |
38 CONFIG_RELEASE = 'Release' | |
39 CONFIGURATIONS = [CONFIG_DEBUG, CONFIG_RELEASE] | |
40 | |
41 | |
42 _RUNGYP_STEP_DESCRIPTION = 'RunGYP' | |
43 _COMPILE_STEP_PREFIX = 'Build' | |
44 _COMPILE_RETRY_PREFIX = 'Retry_' + _COMPILE_STEP_PREFIX | |
45 _COMPILE_NO_WERR_PREFIX = 'Retry_NoWarningsAsErrors_' + _COMPILE_STEP_PREFIX | |
46 | |
47 | |
48 class SkiaFactory(BuildFactory): | |
49 """Encapsulates data and methods common to the Skia master.cfg files.""" | |
50 | |
51 def __init__(self, other_repos=None, do_upload_render_results=False, | |
52 do_upload_bench_results=False, do_patch_step=False, | |
53 build_subdir='skia', target_platform=None, | |
54 configuration=CONFIG_DEBUG, default_timeout=8*60*60, | |
55 deps_target_os=None, environment_variables=None, | |
56 perf_output_basedir=None, builder_name=None, flavor=None, | |
57 make_flags=None, test_args=None, gm_args=None, bench_args=None, | |
58 bench_pictures_cfg='default', compile_warnings_as_errors=False, | |
59 gyp_defines=None, build_targets=None): | |
60 """Instantiates a SkiaFactory as appropriate for this target_platform. | |
61 do_upload_render_results: whether we should upload render results | |
62 do_upload_bench_results: whether we should upload bench results | |
63 do_patch_step: whether the build should include a step which applies a | |
64 patch. This is only applicable for trybots. | |
65 build_subdir: subdirectory to check out and then build within | |
66 other_repos: list of other repositories to also check out (or None). Each | |
67 repo is specified as a tuple: (name, url), where "name" is the target | |
68 directory and "url" is the source code url. | |
69 target_platform: a string such as TARGET_PLATFORM_LINUX | |
70 configuration: 'Debug' or 'Release' | |
71 default_timeout: default timeout for each command, in seconds | |
72 deps_target_os: string; the target_os to be specified in the gclient config. | |
73 environment_variables: dictionary of environment variables that should | |
74 be passed to all commands | |
75 perf_output_basedir: path to directory under which to store performance | |
76 data, or None if we don't want to store performance data | |
77 builder_name: name of the builder associated with this factory | |
78 flavor: which "flavor" of slave-side scripts this factory should use | |
79 make_flags: list of extra flags to pass to the compile step | |
80 test_args: list of extra flags to pass to the 'tests' executable | |
81 gm_args: list of extra flags to pass to the 'gm' executable | |
82 bench_args: list of extra flags to pass to the 'bench' executable | |
83 bench_pictures_cfg: config name to use for bench_pictures | |
84 compile_warnings_as_errors: boolean; whether to build with "-Werror" or | |
85 some equivalent. | |
86 gyp_defines: optional dict; GYP_DEFINES to be used in the build. | |
87 build_targets: optional list; the targets to build. Default is set depending | |
88 on which Build() function is called. | |
89 """ | |
90 properties = {} | |
91 | |
92 self.skipsteps = utils.GetListFromEnvVar( | |
93 config_private.SKIPSTEPS_ENVIRONMENT_VARIABLE) | |
94 self.dontskipsteps = utils.GetListFromEnvVar( | |
95 config_private.DONTSKIPSTEPS_ENVIRONMENT_VARIABLE) | |
96 | |
97 if not make_flags: | |
98 make_flags = [] | |
99 self._make_flags = make_flags | |
100 # Platform-specific stuff. | |
101 if target_platform == TARGET_PLATFORM_WIN32: | |
102 self.TargetPath = ntpath | |
103 else: | |
104 self.TargetPath = posixpath | |
105 | |
106 # Create gclient solutions corresponding to the main build_subdir | |
107 # and other directories we also wish to check out. | |
108 self._gclient_solutions = [gclient_factory.GClientSolution( | |
109 svn_url=SKIA_GIT_URL, name=build_subdir | |
110 ).GetSpec()] | |
111 | |
112 if not other_repos: | |
113 other_repos = [] | |
114 repos_to_checkout = set(other_repos) | |
115 | |
116 for other_repo in repos_to_checkout: | |
117 self._gclient_solutions.append(gclient_factory.GClientSolution( | |
118 svn_url=other_repo[1], name=other_repo[0]).GetSpec()) | |
119 | |
120 self._deps_target_os = deps_target_os | |
121 | |
122 # Set _default_clobber based on config.Master | |
123 self._default_clobber = getattr(config.Master, 'default_clobber', False) | |
124 | |
125 self._do_upload_render_results = do_upload_render_results | |
126 self._do_upload_bench_results = (do_upload_bench_results and | |
127 perf_output_basedir != None) | |
128 self._do_patch_step = do_patch_step | |
129 | |
130 if not environment_variables: | |
131 self._env_vars = {} | |
132 else: | |
133 self._env_vars = dict(environment_variables) | |
134 | |
135 self._gyp_defines = dict(gyp_defines or {}) | |
136 self._gyp_defines['skia_warnings_as_errors'] = \ | |
137 '%d' % int(compile_warnings_as_errors) | |
138 | |
139 self._build_targets = list(build_targets or []) | |
140 | |
141 # Get an implementation of SkiaCommands as appropriate for | |
142 # this target_platform. | |
143 self._workdir = self.TargetPath.join('build', build_subdir) | |
144 self._skia_cmd_obj = skia_commands.SkiaCommands( | |
145 target_platform=target_platform, factory=self, | |
146 configuration=configuration, workdir=self._workdir, | |
147 target_arch=None, default_timeout=default_timeout, | |
148 environment_variables=self._env_vars) | |
149 | |
150 self._perf_output_basedir = perf_output_basedir | |
151 | |
152 self._configuration = configuration | |
153 if self._configuration not in CONFIGURATIONS: | |
154 raise ValueError('Invalid configuration %s. Must be one of: %s' % ( | |
155 self._configuration, CONFIGURATIONS)) | |
156 | |
157 self._skia_svn_username_file = '.skia_svn_username' | |
158 self._skia_svn_password_file = '.skia_svn_password' | |
159 self._builder_name = builder_name | |
160 self._flavor = flavor | |
161 | |
162 def _DetermineRevision(build): | |
163 """ Get the 'revision' property at build time. WithProperties returns the | |
164 empty string if 'revision' is not defined, which causes failures when we | |
165 try to pass the revision over a command line, so we use the string "None" | |
166 to indicate that the revision is not defined. | |
167 | |
168 build: instance of Build for the current build. | |
169 """ | |
170 props = build.getProperties().asDict() | |
171 if props.has_key('revision'): | |
172 if props['revision'][0]: | |
173 return props['revision'][0] | |
174 return 'None' | |
175 | |
176 if not test_args: | |
177 test_args = [] | |
178 if not gm_args: | |
179 gm_args = [] | |
180 if not bench_args: | |
181 bench_args = [] | |
182 | |
183 self._common_args = [ | |
184 # See http://skbug.com/2663 before deleting the next line. | |
185 '--autogen_svn_baseurl', 'bogusValueToFixSkBug2663', | |
186 '--configuration', configuration, | |
187 '--deps_target_os', self._deps_target_os or 'None', | |
188 '--builder_name', builder_name, | |
189 '--build_number', WithProperties('%(buildnumber)s'), | |
190 '--target_platform', target_platform, | |
191 '--revision', WithProperties('%(rev)s', rev=_DetermineRevision), | |
192 '--got_revision', WithProperties('%(got_revision:-None)s'), | |
193 '--perf_output_basedir', perf_output_basedir or 'None', | |
194 '--make_flags', '"%s"' % ' '.join(self._make_flags), | |
195 '--test_args', '"%s' % ' '.join(test_args), | |
196 '--gm_args', '"%s"' % ' '.join(gm_args), | |
197 '--bench_args', '"%s"' % ' '.join(bench_args), | |
198 '--is_try', str(self._do_patch_step), | |
199 '--bench_pictures_cfg', bench_pictures_cfg, | |
200 '--issue_number', WithProperties('%(issue:-None)s'), | |
201 ] | |
202 BuildFactory.__init__(self, build_factory_properties=properties) | |
203 | |
204 def Validate(self): | |
205 """ Validate the Factory against the known good configuration. """ | |
206 test_dir = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, | |
207 'tools', 'tests', 'factory_configuration') | |
208 | |
209 # Write the actual configuration. | |
210 actual_dir = os.path.join(test_dir, 'actual') | |
211 if not os.path.exists(actual_dir): | |
212 os.makedirs(actual_dir) | |
213 self_as_string = utils.ToString(self.__dict__) | |
214 with open(os.path.join(actual_dir, self._builder_name), 'w') as f: | |
215 f.write(self_as_string) | |
216 | |
217 # Read the expected configuration. | |
218 expected_dir = os.path.join(test_dir, 'expected') | |
219 try: | |
220 expectation = open(os.path.join(expected_dir, self._builder_name)).read() | |
221 except IOError: | |
222 msg = 'No expected factory configuration for %s in %s.' % ( | |
223 self._builder_name, expected_dir) | |
224 if config_private.die_on_validation_failure: | |
225 raise Exception(msg) | |
226 else: | |
227 print 'Warning: %s' % msg | |
228 return | |
229 | |
230 # Compare actual to expected. | |
231 if self_as_string != expectation: | |
232 if config_private.die_on_validation_failure: | |
233 raise ValueError('Factory configuration for %s does not match ' | |
234 'expectation in %s! Here\'s the diff:\n%s\n' % | |
235 (self._builder_name, expected_dir, | |
236 utils.StringDiff(expectation, self_as_string))) | |
237 else: | |
238 # We don't print the full diff in this case because: | |
239 # a. It's generally too long to be easily read in a terminal | |
240 # b. All of the printing can noticeably slow down the master startup | |
241 # c. The master prints so much output that it would be easy to miss the | |
242 # diff if we did print it. | |
243 print 'Warning: Factory configuration for %s does not match ' \ | |
244 'expectation!' % self._builder_name | |
245 | |
246 # TODO(borenet): Can kwargs be used to simplify this function declaration? | |
247 def AddSlaveScript(self, script, description, args=None, timeout=None, | |
248 halt_on_failure=False, | |
249 is_upload_render_step=False, is_upload_bench_step=False, | |
250 is_rebaseline_step=False, get_props_from_stdout=None, | |
251 workdir=None, do_step_if=None, always_run=False, | |
252 flunk_on_failure=True, exception_on_failure=False): | |
253 """ Add a BuildStep consisting of a python script. | |
254 | |
255 script: which slave-side python script to run. | |
256 description: string briefly describing the BuildStep; if this description | |
257 is in the self.skipsteps list, this BuildStep will be skipped--unless | |
258 it's in the self.dontskipsteps list, in which case we run it! | |
259 args: optional list of strings; arguments to pass to the script. | |
260 timeout: optional integer; maximum time for the BuildStep to complete. | |
261 halt_on_failure: boolean indicating whether to continue the build if this | |
262 step fails. | |
263 is_upload_render_step: boolean; if true, only run if | |
264 self._do_upload_render_results is True | |
265 is_upload_bench_step: boolean; if true, only run if | |
266 self._do_upload_bench_results is True | |
267 is_rebaseline_step: boolean indicating whether this step is required for | |
268 rebaseline-only builds. | |
269 get_props_from_stdout: optional dictionary. Keys are strings indicating | |
270 build properties to set based on the output of this step. Values are | |
271 strings containing regular expressions for parsing the property from | |
272 the output of the step. | |
273 workdir: optional string indicating the working directory in which to run | |
274 the script. If this is provided, then the script must be given relative | |
275 to this directory. | |
276 do_step_if: optional, function which determines whether or not to run the | |
277 step. The function is not evaluated until runtime. | |
278 always_run: boolean indicating whether this step should run even if a | |
279 previous step which had halt_on_failure has failed. | |
280 flunk_on_failure: boolean indicating whether the whole build fails if this | |
281 step fails. | |
282 exception_on_failure: boolean indicating whether to raise an exception if | |
283 this step fails. This causes the step to go purple instead of red, and | |
284 causes the build to stop. Should be used if the build step's failure is | |
285 typically transient or results from an infrastructure failure rather | |
286 than a code change. | |
287 """ | |
288 if description not in self.dontskipsteps: | |
289 if description in self.skipsteps: | |
290 return | |
291 if is_upload_render_step and not self._do_upload_render_results: | |
292 return | |
293 if is_upload_bench_step and not self._do_upload_bench_results: | |
294 return | |
295 | |
296 arguments = list(self._common_args) | |
297 if args: | |
298 arguments += args | |
299 self._skia_cmd_obj.AddSlaveScript( | |
300 script=script, | |
301 args=arguments, | |
302 description=description, | |
303 timeout=timeout, | |
304 halt_on_failure=halt_on_failure, | |
305 is_upload_step=is_upload_render_step or is_upload_bench_step, | |
306 is_rebaseline_step=is_rebaseline_step, | |
307 get_props_from_stdout=get_props_from_stdout, | |
308 workdir=workdir, | |
309 do_step_if=do_step_if, | |
310 always_run=always_run, | |
311 flunk_on_failure=flunk_on_failure, | |
312 exception_on_failure=exception_on_failure) | |
313 | |
314 def AddFlavoredSlaveScript(self, script, args=None, **kwargs): | |
315 """ Add a flavor-specific BuildStep. | |
316 | |
317 Finds a script to run by concatenating the flavor of this BuildFactory with | |
318 the provided script name. | |
319 """ | |
320 flavor_args = ['--flavor', self._flavor or 'default'] | |
321 self.AddSlaveScript(script, args=list(args or []) + flavor_args, **kwargs) | |
322 | |
323 def RunGYP(self, description=_RUNGYP_STEP_DESCRIPTION, do_step_if=None): | |
324 """ Run GYP to generate build files. | |
325 | |
326 description: string; description of this BuildStep. | |
327 do_step_if: optional, function which determines whether or not to run this | |
328 step. | |
329 """ | |
330 self.AddFlavoredSlaveScript(script='run_gyp.py', description=description, | |
331 halt_on_failure=True, do_step_if=do_step_if, | |
332 args=['--gyp_defines', | |
333 ' '.join('%s=%s' % (k, v) for k, v in | |
334 self._gyp_defines.items())]) | |
335 | |
336 def Make(self, target, description, is_rebaseline_step=False, do_step_if=None, | |
337 always_run=False, flunk_on_failure=True, halt_on_failure=True): | |
338 """ Build a single target. | |
339 | |
340 target: string; the target to build. | |
341 description: string; description of this BuildStep. | |
342 is_rebaseline_step: optional boolean; whether or not this step is required | |
343 for rebaseline-only builds. | |
344 do_step_if: optional, function which determines whether or not to run this | |
345 step. | |
346 always_run: boolean indicating whether this step should run even if a | |
347 previous step which had halt_on_failure has failed. | |
348 flunk_on_failure: boolean indicating whether the whole build fails if this | |
349 step fails. | |
350 halt_on_failure: boolean indicating whether to continue the build if this | |
351 step fails. | |
352 """ | |
353 args = ['--target', target, | |
354 '--gyp_defines', | |
355 ' '.join('%s=%s' % (k, v) for k, v in self._gyp_defines.items())] | |
356 self.AddFlavoredSlaveScript(script='compile.py', args=args, | |
357 description=description, | |
358 halt_on_failure=halt_on_failure, | |
359 is_rebaseline_step=is_rebaseline_step, | |
360 do_step_if=do_step_if, | |
361 always_run=always_run, | |
362 flunk_on_failure=flunk_on_failure) | |
363 | |
364 def Compile(self, clobber=None, retry_with_clobber_on_failure=True, | |
365 retry_without_werr_on_failure=False): | |
366 """ Compile step. Build everything. | |
367 | |
368 clobber: optional boolean; whether to 'clean' before building. | |
369 retry_with_clobber_on_failure: optional boolean; if the build fails, clean | |
370 and try again, with the same configuration as before. | |
371 retry_without_werr_on_failure: optional boolean; if the build fails, clean | |
372 and try again *without* warnings-as-errors. | |
373 """ | |
374 if clobber is None: | |
375 clobber = self._default_clobber | |
376 | |
377 | |
378 if clobber: | |
379 self.AddFlavoredSlaveScript(script='clean.py', description='Clean', | |
380 halt_on_failure=True) | |
381 | |
382 # Only retry with clobber if we've requested it AND we aren't clobbering on | |
383 # the first build. | |
384 maybe_retry_with_clobber = retry_with_clobber_on_failure and not clobber | |
385 | |
386 def ShouldRetryWithClobber(step): | |
387 """ Determine whether the retry step should run. """ | |
388 if not maybe_retry_with_clobber: | |
389 return False | |
390 gyp_or_compile_failed = False | |
391 retry_failed = False | |
392 for build_step in step.build.getStatus().getSteps(): | |
393 if (build_step.isFinished() and | |
394 build_step.getResults()[0] == builder.FAILURE): | |
395 if build_step.getName().startswith(_COMPILE_STEP_PREFIX): | |
396 gyp_or_compile_failed = True | |
397 elif build_step.getName() == _RUNGYP_STEP_DESCRIPTION: | |
398 gyp_or_compile_failed = True | |
399 elif build_step.getName().startswith(_COMPILE_RETRY_PREFIX): | |
400 retry_failed = True | |
401 return gyp_or_compile_failed and not retry_failed | |
402 | |
403 def ShouldRetryWithoutWarnings(step): | |
404 """ Determine whether the retry-without-warnings-as-errors step should | |
405 run. """ | |
406 if not retry_without_werr_on_failure: | |
407 return False | |
408 gyp_or_compile_failed = False | |
409 retry_failed = False | |
410 no_warning_retry_failed = False | |
411 for build_step in step.build.getStatus().getSteps(): | |
412 if (build_step.isFinished() and | |
413 build_step.getResults()[0] == builder.FAILURE): | |
414 if build_step.getName().startswith(_COMPILE_STEP_PREFIX): | |
415 gyp_or_compile_failed = True | |
416 elif build_step.getName().startswith(_COMPILE_RETRY_PREFIX): | |
417 retry_failed = True | |
418 elif build_step.getName().startswith( | |
419 _COMPILE_NO_WERR_PREFIX): | |
420 no_warning_retry_failed = True | |
421 # If we've already failed a previous retry without warnings, just give up. | |
422 if no_warning_retry_failed: | |
423 return False | |
424 # If we're retrying with clobber, only retry without warnings if a clobber | |
425 # retry has failed. | |
426 if maybe_retry_with_clobber: | |
427 return retry_failed | |
428 # Only run the retry if the initial compile has failed. | |
429 return gyp_or_compile_failed | |
430 | |
431 for build_target in self._build_targets: | |
432 self.Make(target=build_target, | |
433 description=_COMPILE_STEP_PREFIX + \ | |
434 utils.UnderscoresToCapWords(build_target), | |
435 flunk_on_failure=not maybe_retry_with_clobber, | |
436 halt_on_failure=(not maybe_retry_with_clobber and | |
437 not retry_without_werr_on_failure)) | |
438 | |
439 # Try again with a clean build. | |
440 self.AddFlavoredSlaveScript(script='clean.py', description='Clean', | |
441 do_step_if=ShouldRetryWithClobber) | |
442 for build_target in self._build_targets: | |
443 self.Make(target=build_target, | |
444 description=_COMPILE_RETRY_PREFIX + \ | |
445 utils.UnderscoresToCapWords(build_target), | |
446 flunk_on_failure=True, | |
447 halt_on_failure=not retry_without_werr_on_failure, | |
448 do_step_if=ShouldRetryWithClobber) | |
449 | |
450 # Try again without warnings-as-errors. | |
451 self._gyp_defines['skia_warnings_as_errors'] = '0' | |
452 self.AddFlavoredSlaveScript(script='clean.py', description='Clean', | |
453 always_run=True, | |
454 do_step_if=ShouldRetryWithoutWarnings) | |
455 for build_target in self._build_targets: | |
456 self.Make(target=build_target, | |
457 description=_COMPILE_NO_WERR_PREFIX + \ | |
458 utils.UnderscoresToCapWords(build_target), | |
459 flunk_on_failure=True, | |
460 halt_on_failure=True, | |
461 do_step_if=ShouldRetryWithoutWarnings) | |
462 | |
463 def Install(self): | |
464 """ Install the compiled executables. """ | |
465 self.AddFlavoredSlaveScript(script='install.py', description='Install', | |
466 halt_on_failure=True, exception_on_failure=True) | |
467 | |
468 def DownloadSKPs(self): | |
469 """ Download the SKPs. """ | |
470 self.AddSlaveScript(script='download_skps.py', description='DownloadSKPs', | |
471 halt_on_failure=True, exception_on_failure=True) | |
472 | |
473 def DownloadSKImageFiles(self): | |
474 """ Download image files for running skimage. """ | |
475 self.AddSlaveScript(script='download_skimage_files.py', | |
476 description='DownloadSKImageFiles', | |
477 halt_on_failure=True, exception_on_failure=True) | |
478 | |
479 def RunDecodingTests(self): | |
480 """ Run tests of image decoders. """ | |
481 self.AddFlavoredSlaveScript(script='run_decoding_tests.py', | |
482 description='RunDecodingTests') | |
483 | |
484 def RunDM(self): | |
485 """Run DM.""" | |
486 self.AddFlavoredSlaveScript('run_dm.py', description='RunDM') | |
487 | |
488 def RunGM(self): | |
489 """ Run the "GM" tool, saving the images to disk. """ | |
490 self.AddFlavoredSlaveScript(script='run_gm.py', description='GenerateGMs', | |
491 is_rebaseline_step=True) | |
492 | |
493 def PreRender(self): | |
494 """ Step to run before the render steps. """ | |
495 self.AddFlavoredSlaveScript(script='prerender.py', description='PreRender', | |
496 exception_on_failure=True) | |
497 | |
498 def RenderSKPs(self): | |
499 """ Generate images from .skp's. """ | |
500 self.AddFlavoredSlaveScript(script='render_skps.py', | |
501 description='RenderSKPs') | |
502 | |
503 def RenderPdfs(self): | |
504 """ Run the "render_pdfs" tool to generate pdfs from .skp's. """ | |
505 self.AddFlavoredSlaveScript(script='render_pdfs.py', | |
506 description='RenderPdfs') | |
507 | |
508 def PostRender(self): | |
509 """ Step to run after the render steps. """ | |
510 self.AddFlavoredSlaveScript(script='postrender.py', | |
511 description='PostRender', | |
512 exception_on_failure=True) | |
513 | |
514 def PreBench(self): | |
515 """ Step to run before the benchmarking steps. """ | |
516 self.AddFlavoredSlaveScript(script='prebench.py', | |
517 description='PreBench', | |
518 exception_on_failure=True) | |
519 | |
520 def PostBench(self): | |
521 """ Step to run after the benchmarking steps. """ | |
522 self.AddFlavoredSlaveScript(script='postbench.py', | |
523 description='PostBench', | |
524 exception_on_failure=True) | |
525 | |
526 def CompareGMs(self): | |
527 """Compare the actually-generated GM images to the checked-in baselines.""" | |
528 self.AddSlaveScript(script='compare_gms.py', | |
529 description='CompareGMs', | |
530 get_props_from_stdout={ | |
531 'latest_gm_failures_url': | |
532 '%s([^\n]*)\n' % skia_vars.GetGlobalVariable( | |
533 'latest_gm_failures_preamble')}, | |
534 is_rebaseline_step=True) | |
535 | |
536 def CompareRenderedSKPs(self): | |
537 """Compare the actual image results of SKP rendering to expectations.""" | |
538 self.AddSlaveScript(script='compare_rendered_skps.py', | |
539 description='CompareRenderedSKPs', | |
540 is_rebaseline_step=True) | |
541 | |
542 def RunNanobench(self): | |
543 """ Run "nanobench" """ | |
544 self.AddFlavoredSlaveScript(script='run_nanobench.py', | |
545 description='RunNanobench') | |
546 | |
547 def UpdateScripts(self): | |
548 """ Update the buildbot scripts on the build slave. | |
549 | |
550 Only runs in production. See http://skbug.com/2432 | |
551 """ | |
552 description = 'UpdateScripts' | |
553 if ((config_private.Master.get_active_master().is_production_host) or | |
554 (description in self.dontskipsteps)): | |
555 self.AddSlaveScript( | |
556 script=self.TargetPath.join('..', '..', '..', '..', | |
557 '..', 'slave', | |
558 'skia_slave_scripts', | |
559 'update_scripts.py'), | |
560 description=description, | |
561 halt_on_failure=True, | |
562 get_props_from_stdout={'buildbot_revision': | |
563 'Skiabot scripts updated to (\w+)'}, | |
564 workdir='build', | |
565 exception_on_failure=True) | |
566 | |
567 def Update(self): | |
568 """ Update the Skia code on the build slave. """ | |
569 args = ['--gclient_solutions', '"%s"' % self._gclient_solutions] | |
570 self.AddSlaveScript( | |
571 script=self.TargetPath.join('..', '..', '..', '..', '..', 'slave', | |
572 'skia_slave_scripts', 'update.py'), | |
573 description='Update', | |
574 args=args, | |
575 timeout=None, | |
576 halt_on_failure=True, | |
577 is_rebaseline_step=True, | |
578 get_props_from_stdout={'got_revision':'Skia updated to (\w+)'}, | |
579 workdir='build', | |
580 exception_on_failure=True) | |
581 | |
582 def ApplyPatch(self, alternate_workdir=None, alternate_script=None): | |
583 """ Apply a patch to the Skia code on the build slave. """ | |
584 def _GetPatch(build): | |
585 """Find information about the patch (if any) to apply. | |
586 | |
587 Returns: | |
588 An encoded string containing a tuple of the form (level, url) which | |
589 indicates where to go to download the patch. | |
590 """ | |
591 if build.getSourceStamp().patch and \ | |
592 'patch_file_url' in build.getProperties(): | |
593 # The presence of a patch attached to the Source Stamp and the | |
594 # 'patch_file_url' build property indicate that the patch came from the | |
595 # skia_try repo, and was probably submitted using the submit_try script | |
596 # or "gcl/git-cl try". | |
597 patch = (build.getSourceStamp().patch[0], | |
598 build.getProperty('patch_file_url')) | |
599 return str(patch).encode() | |
600 elif 'issue' in build.getProperties() and \ | |
601 'patchset' in build.getProperties(): | |
602 # The presence of the 'issue' and 'patchset' build properties indicates | |
603 # that the patch came from Rietveld. | |
604 # Don't assume that the 'issue' and 'patchset' build properties are | |
605 # always integer type, even though they should contain integers. | |
606 patch = '%s/download/issue%s_%s.diff' % ( | |
607 config_private.CODE_REVIEW_SITE.rstrip('/'), | |
608 build.getProperty('issue'), | |
609 build.getProperty('patchset')) | |
610 # If the patch came from Rietveld, assume it came from a git repo and | |
611 # therefore it has a patch level of 1. If this isn't the case, the | |
612 # slave-side script should detect it and use level 0 instead. | |
613 return str((1, patch)).encode() | |
614 else: | |
615 patch = 'None' | |
616 return patch | |
617 | |
618 if not bool(alternate_workdir) == bool(alternate_script): | |
619 raise ValueError('alternate_workdir and alternate_script must be provided' | |
620 ' together.') | |
621 args = ['--patch', WithProperties('%(patch)s', patch=_GetPatch)] | |
622 if alternate_script: | |
623 self.AddSlaveScript(script=alternate_script, | |
624 description='ApplyPatch', | |
625 args=args, | |
626 halt_on_failure=True, | |
627 workdir=alternate_workdir, | |
628 exception_on_failure=True) | |
629 else: | |
630 self.AddSlaveScript(script='apply_patch.py', | |
631 description='ApplyPatch', | |
632 args=args, | |
633 halt_on_failure=True, | |
634 exception_on_failure=True) | |
635 | |
636 def UpdateSteps(self): | |
637 """ Update the Skia sources. """ | |
638 self.UpdateScripts() | |
639 self.Update() | |
640 if self._do_patch_step: | |
641 self.ApplyPatch() | |
642 | |
643 def UploadBenchResults(self): | |
644 """ Upload bench results (performance data). """ | |
645 self.AddSlaveScript(script='upload_bench_results.py', | |
646 description='UploadBenchResults', | |
647 exception_on_failure=True, | |
648 is_upload_bench_step=True) | |
649 | |
650 def UploadBenchResultsToAppEngine(self): | |
651 """ Upload bench results (performance data) to AppEngine. """ | |
652 self.AddSlaveScript(script='upload_bench_results_appengine.py', | |
653 description='UploadBenchResultsToAppengine', | |
654 exception_on_failure=True) | |
655 | |
656 def UploadWebpagePictureBenchResults(self): | |
657 """ Upload webpage picture bench results (performance data). """ | |
658 self.AddSlaveScript(script='upload_webpage_picture_bench_results.py', | |
659 description='UploadWebpagePictureBenchResults', | |
660 exception_on_failure=True) | |
661 | |
662 | |
663 def UploadGMResults(self): | |
664 """ Upload the images generated by GM """ | |
665 self.AddSlaveScript(script='upload_gm_results.py', | |
666 description='UploadGMResults', timeout=5400, | |
667 is_upload_render_step=True, is_rebaseline_step=True, | |
668 exception_on_failure=True) | |
669 | |
670 def UploadRenderedSKPs(self): | |
671 """Upload the actual image results of SKP rendering.""" | |
672 self.AddSlaveScript(script='upload_rendered_skps.py', | |
673 description='UploadRenderedSKPs', | |
674 is_upload_render_step=True, is_rebaseline_step=True) | |
675 | |
676 def UploadSKImageResults(self): | |
677 self.AddSlaveScript(script='upload_skimage_results.py', | |
678 description='UploadSKImageResults', | |
679 is_upload_render_step=True, | |
680 exception_on_failure=True) | |
681 | |
682 def CommonSteps(self, clobber=None): | |
683 """ Steps which are run at the beginning of all builds. """ | |
684 self.UpdateSteps() | |
685 self.DownloadSKPs() | |
686 self.Compile(clobber) | |
687 self.Install() | |
688 | |
689 def NonPerfSteps(self): | |
690 """ Add correctness testing BuildSteps. """ | |
691 self.DownloadSKImageFiles() | |
692 self.PreRender() | |
693 self.RunDM() | |
694 self.RunGM() | |
695 self.RenderSKPs() | |
696 self.RenderPdfs() | |
697 self.RunDecodingTests() | |
698 self.PostRender() | |
699 self.UploadGMResults() | |
700 self.UploadRenderedSKPs() | |
701 self.UploadSKImageResults() | |
702 self.CompareGMs() | |
703 self.CompareRenderedSKPs() | |
704 | |
705 def PerfSteps(self): | |
706 """ Add performance testing BuildSteps. """ | |
707 self.PreBench() | |
708 self.RunNanobench() | |
709 self.PostBench() | |
710 self.UploadBenchResults() | |
711 | |
712 def Build(self, role=None, clobber=None): | |
713 """Build and return the complete BuildFactory. | |
714 | |
715 role: string; the intended role of this builder. The role affects which | |
716 steps are run. Known values are given in the utils module. | |
717 clobber: boolean indicating whether we should clean before building | |
718 """ | |
719 # Special case: for the ZeroGPUCache bot, we only run GM. | |
720 if 'ZeroGPUCache' in self._builder_name: | |
721 self._build_targets = ['gm'] | |
722 self.UpdateSteps() | |
723 self.Compile(clobber) | |
724 self.Install() | |
725 self.PreRender() | |
726 self.RunGM() | |
727 self.PostRender() | |
728 elif ('TSAN' in self._builder_name and | |
729 role == builder_name_schema.BUILDER_ROLE_TEST): | |
730 self._build_targets = ['dm'] | |
731 self.UpdateSteps() | |
732 self.Compile(clobber) | |
733 self.Install() | |
734 self.RunDM() | |
735 elif ('Valgrind' in self._builder_name and | |
736 role == builder_name_schema.BUILDER_ROLE_TEST): | |
737 if not self._build_targets: | |
738 self._build_targets = ['most'] | |
739 self.CommonSteps(clobber) | |
740 # TODO(borenet):When https://code.google.com//p/skia/issues/detail?id=1711 | |
741 # is fixed, run self.NonPerfSteps() instead of the below steps. | |
742 self.DownloadSKImageFiles() | |
743 self.PreRender() | |
744 self.RunDM() | |
745 self.RunGM() | |
746 self.RenderSKPs() | |
747 self.RenderPdfs() | |
748 self.RunDecodingTests() | |
749 self.PostRender() | |
750 # (end steps which need to be replaced once #1711 is fixed) | |
751 self.PreBench() | |
752 self.RunNanobench() | |
753 self.PostBench() | |
754 elif not role: | |
755 # If no role is provided, just run everything. | |
756 if not self._build_targets: | |
757 self._build_targets = ['most'] | |
758 self.CommonSteps(clobber) | |
759 self.NonPerfSteps() | |
760 self.PerfSteps() | |
761 elif role == builder_name_schema.BUILDER_ROLE_BUILD: | |
762 # Compile-only builder. | |
763 self.UpdateSteps() | |
764 if not self._build_targets: | |
765 self._build_targets = [] | |
766 if (('Win' in self._builder_name) or | |
767 ('Ubuntu' in self._builder_name and 'x86-' in self._builder_name) or | |
768 'Mac10.6' in self._builder_name or 'Mac10.7' in self._builder_name): | |
769 # Don't compile the debugger on Windows, Mac 10.6, Mac 10.7, or | |
770 # 32-bit Linux since the Qt SDK doesn't include libraries for those | |
771 # platforms. | |
772 self._build_targets.append('most') | |
773 else: | |
774 self._build_targets.append('everything') | |
775 self.Compile(clobber=clobber, | |
776 retry_without_werr_on_failure=True) | |
777 else: | |
778 if not self._build_targets: | |
779 self._build_targets = ['most'] | |
780 self.CommonSteps(clobber) | |
781 if role == builder_name_schema.BUILDER_ROLE_TEST: | |
782 # Test-running builder. | |
783 self.NonPerfSteps() | |
784 if self._configuration == CONFIG_DEBUG: | |
785 # Debug-mode testers run all steps, but release-mode testers don't. | |
786 self.PerfSteps() | |
787 elif role == builder_name_schema.BUILDER_ROLE_PERF: | |
788 # Perf-only builder. | |
789 if not self._perf_output_basedir: | |
790 raise ValueError( | |
791 'BuildPerfOnly requires perf_output_basedir to be defined.') | |
792 if self._configuration != CONFIG_RELEASE: | |
793 raise ValueError('BuildPerfOnly should run in %s configuration.' % | |
794 CONFIG_RELEASE) | |
795 self.PerfSteps() | |
796 self.Validate() | |
797 return self | |
OLD | NEW |