| OLD | NEW |
| 1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 | 5 |
| 6 """Miscellaneous utilities needed by the Skia buildbot master.""" | 6 """Miscellaneous utilities needed by the Skia buildbot master.""" |
| 7 | 7 |
| 8 | 8 |
| 9 import httplib2 | 9 import httplib2 |
| 10 import re | 10 import re |
| 11 | 11 |
| 12 # requires Google APIs client library for Python; see | 12 # requires Google APIs client library for Python; see |
| 13 # https://code.google.com/p/google-api-python-client/wiki/Installation | 13 # https://code.google.com/p/google-api-python-client/wiki/Installation |
| 14 from apiclient.discovery import build | 14 from apiclient.discovery import build |
| 15 from buildbot.scheduler import AnyBranchScheduler | 15 from buildbot.scheduler import AnyBranchScheduler |
| 16 from buildbot.schedulers import timed | 16 from buildbot.schedulers import timed |
| 17 from buildbot.schedulers.filter import ChangeFilter | 17 from buildbot.schedulers.filter import ChangeFilter |
| 18 from buildbot.util import NotABranch | 18 from buildbot.util import NotABranch |
| 19 from config_private import TRY_SVN_BASEURL | 19 from config_private import TRY_SVN_BASEURL |
| 20 from master import master_config | 20 from master import master_config |
| 21 from master import try_job_svn | 21 from master import try_job_svn |
| 22 from master import try_job_rietveld | 22 from master import try_job_rietveld |
| 23 from master.builders_pools import BuildersPools | 23 from master.builders_pools import BuildersPools |
| 24 from oauth2client.client import SignedJwtAssertionCredentials | 24 from oauth2client.client import SignedJwtAssertionCredentials |
| 25 | 25 |
| 26 import config_private | 26 import config_private |
| 27 | 27 |
| 28 | 28 |
| 29 BUILDER_NAME_SEP = '-' |
| 30 |
| 31 # Patterns for creating builder names, based on the role of the builder. |
| 32 # TODO(borenet): Extract these into a separate file (JSON?) so that they can be |
| 33 # read by other users. |
| 34 BUILDER_ROLE_COMPILE = 'Build' |
| 35 BUILDER_ROLE_PERF = 'Perf' |
| 36 BUILDER_ROLE_TEST = 'Test' |
| 37 BUILDER_ROLE_HOUSEKEEPER = 'Housekeeper' |
| 38 BUILDER_NAME_DEFAULT_ATTRS = ['os', 'model', 'gpu', 'arch', 'configuration'] |
| 39 BUILDER_NAME_SCHEMA = { |
| 40 BUILDER_ROLE_COMPILE: ['os', 'compiler', 'target_arch', 'configuration'], |
| 41 BUILDER_ROLE_TEST: BUILDER_NAME_DEFAULT_ATTRS, |
| 42 BUILDER_ROLE_PERF: BUILDER_NAME_DEFAULT_ATTRS, |
| 43 BUILDER_ROLE_HOUSEKEEPER: ['frequency'], |
| 44 } |
| 45 |
| 29 CATEGORY_BUILD = ' Build' | 46 CATEGORY_BUILD = ' Build' |
| 30 TRYBOT_NAME_SUFFIX = '_Trybot' | 47 TRYBOT_NAME_SUFFIX = 'Trybot' |
| 31 TRY_SCHEDULER_SVN = 'skia_try_svn' | 48 TRY_SCHEDULER_SVN = 'skia_try_svn' |
| 32 TRY_SCHEDULER_RIETVELD = 'skia_try_rietveld' | 49 TRY_SCHEDULER_RIETVELD = 'skia_try_rietveld' |
| 33 TRY_SCHEDULERS = [TRY_SCHEDULER_SVN, TRY_SCHEDULER_RIETVELD] | 50 TRY_SCHEDULERS = [TRY_SCHEDULER_SVN, TRY_SCHEDULER_RIETVELD] |
| 34 TRY_SCHEDULERS_STR = '|'.join(TRY_SCHEDULERS) | 51 TRY_SCHEDULERS_STR = '|'.join(TRY_SCHEDULERS) |
| 35 | 52 |
| 36 | 53 |
| 37 def IsTrybot(builder_name): | 54 def IsTrybot(builder_name): |
| 38 return builder_name.endswith(TRYBOT_NAME_SUFFIX) | 55 return builder_name.endswith(TRYBOT_NAME_SUFFIX) |
| 39 | 56 |
| 40 | 57 |
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 242 # Skip buildbot runs of a CL if its commit log message contains the following | 259 # Skip buildbot runs of a CL if its commit log message contains the following |
| 243 # substring. | 260 # substring. |
| 244 SKIP_BUILDBOT_SUBSTRING = '(SkipBuildbotRuns)' | 261 SKIP_BUILDBOT_SUBSTRING = '(SkipBuildbotRuns)' |
| 245 | 262 |
| 246 # If the below regex is found in a CL's commit log message, only run the | 263 # If the below regex is found in a CL's commit log message, only run the |
| 247 # builders specified therein. | 264 # builders specified therein. |
| 248 RUN_BUILDERS_REGEX = '\(RunBuilders:(.+)\)' | 265 RUN_BUILDERS_REGEX = '\(RunBuilders:(.+)\)' |
| 249 RUN_BUILDERS_RE_COMPILED = re.compile(RUN_BUILDERS_REGEX) | 266 RUN_BUILDERS_RE_COMPILED = re.compile(RUN_BUILDERS_REGEX) |
| 250 | 267 |
| 251 | 268 |
| 269 def AndroidModelToDevice(android_model): |
| 270 """ Converts Android model names to device names which android_setup.sh will |
| 271 like. |
| 272 |
| 273 Examples: |
| 274 'NexusS' becomes 'nexus_s' |
| 275 'Nexus10' becomes 'nexus_10' |
| 276 |
| 277 android_model: string; model name for an Android device. |
| 278 """ |
| 279 name_parts = [] |
| 280 for part in re.split('(\d+)', android_model): |
| 281 if re.match('(\d+)', part): |
| 282 name_parts.append(part) |
| 283 else: |
| 284 name_parts.extend(re.findall('[A-Z][a-z]*', part)) |
| 285 return '_'.join([part.lower() for part in name_parts]) |
| 286 |
| 287 |
| 252 # Since we can't modify the existing Helper class, we subclass it here, | 288 # Since we can't modify the existing Helper class, we subclass it here, |
| 253 # overriding the necessary parts to get things working as we want. | 289 # overriding the necessary parts to get things working as we want. |
| 254 # Specifically, the Helper class hardcodes each registered scheduler to be | 290 # Specifically, the Helper class hardcodes each registered scheduler to be |
| 255 # instantiated as a 'Scheduler,' which aliases 'SingleBranchScheduler.' We add | 291 # instantiated as a 'Scheduler,' which aliases 'SingleBranchScheduler.' We add |
| 256 # an 'AnyBranchScheduler' method and change the implementation of Update() to | 292 # an 'AnyBranchScheduler' method and change the implementation of Update() to |
| 257 # instantiate the proper type. | 293 # instantiate the proper type. |
| 258 | 294 |
| 259 # TODO(borenet): modify this code upstream so that we don't need this override. | 295 # TODO(borenet): modify this code upstream so that we don't need this override. |
| 260 # BUG: http://code.google.com/p/skia/issues/detail?id=761 | 296 # BUG: http://code.google.com/p/skia/issues/detail?id=761 |
| 261 class SkiaHelper(master_config.Helper): | 297 class SkiaHelper(master_config.Helper): |
| 262 | 298 |
| 263 def Builder(self, name, factory, gatekeeper=None, scheduler=None, | 299 def Builder(self, name, factory, gatekeeper=None, scheduler=None, |
| 264 builddir=None, auto_reboot=False, notify_on_missing=False, | 300 builddir=None, auto_reboot=False, notify_on_missing=False): |
| 265 override_category=None): | |
| 266 if override_category: | |
| 267 old_category = self._defaults.get('category') | |
| 268 self._defaults['category'] = override_category | |
| 269 super(SkiaHelper, self).Builder(name=name, factory=factory, | 301 super(SkiaHelper, self).Builder(name=name, factory=factory, |
| 270 gatekeeper=gatekeeper, scheduler=scheduler, | 302 gatekeeper=gatekeeper, scheduler=scheduler, |
| 271 builddir=builddir, auto_reboot=auto_reboot, | 303 builddir=builddir, auto_reboot=auto_reboot, |
| 272 notify_on_missing=notify_on_missing) | 304 notify_on_missing=notify_on_missing) |
| 273 if override_category: | |
| 274 self._defaults['category'] = old_category | |
| 275 | 305 |
| 276 def AnyBranchScheduler(self, name, branches, treeStableTimer=60, | 306 def AnyBranchScheduler(self, name, branches, treeStableTimer=60, |
| 277 categories=None): | 307 categories=None): |
| 278 if name in self._schedulers: | 308 if name in self._schedulers: |
| 279 raise ValueError('Scheduler %s already exist' % name) | 309 raise ValueError('Scheduler %s already exist' % name) |
| 280 self._schedulers[name] = {'type': 'AnyBranchScheduler', | 310 self._schedulers[name] = {'type': 'AnyBranchScheduler', |
| 281 'branches': branches, | 311 'branches': branches, |
| 282 'treeStableTimer': treeStableTimer, | 312 'treeStableTimer': treeStableTimer, |
| 283 'builders': [], | 313 'builders': [], |
| 284 'categories': categories} | 314 'categories': categories} |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 406 # been defined. | 436 # been defined. |
| 407 # pylint: disable=W0601 | 437 # pylint: disable=W0601 |
| 408 global skia_all_subdirs | 438 global skia_all_subdirs |
| 409 try: | 439 try: |
| 410 if skia_all_subdirs: | 440 if skia_all_subdirs: |
| 411 raise Exception('skia_all_subdirs has already been defined!') | 441 raise Exception('skia_all_subdirs has already been defined!') |
| 412 except NameError: | 442 except NameError: |
| 413 skia_all_subdirs = all_subdirs | 443 skia_all_subdirs = all_subdirs |
| 414 | 444 |
| 415 | 445 |
| 416 def MakeBuilderName(builder_base_name, config): | 446 def MakeBuilderName(role, extra_config=None, is_trybot=False, **kwargs): |
| 417 """ Inserts config into builder_base_name at '%s', or if builder_base_name | 447 schema = BUILDER_NAME_SCHEMA.get(role) |
| 418 does not contain '%s', appends config to the end of builder_base_name, | 448 if not schema: |
| 419 separated by an underscore. """ | 449 raise ValueError('%s is not a recognized role.' % role) |
| 420 try: | 450 for k, v in kwargs.iteritems(): |
| 421 return builder_base_name % config | 451 if BUILDER_NAME_SEP in v: |
| 422 except TypeError: | 452 raise ValueError('%s not allowed in %s.' % (v, BUILDER_NAME_SEP)) |
| 423 # If builder_base_name does not contain '%s' | 453 if not k in schema: |
| 424 return '%s_%s' % (builder_base_name, config) | 454 raise ValueError('Schema does not contain "%s": %s' %(k, schema)) |
| 455 if extra_config and BUILDER_NAME_SEP in extra_config: |
| 456 raise ValueError('%s not allowed in %s.' % (extra_config, |
| 457 BUILDER_NAME_SEP)) |
| 458 name_parts = [role] |
| 459 name_parts.extend([kwargs[attribute] for attribute in schema]) |
| 460 if extra_config: |
| 461 name_parts.append(extra_config) |
| 462 if is_trybot: |
| 463 name_parts.append(TRYBOT_NAME_SUFFIX) |
| 464 print BUILDER_NAME_SEP.join(name_parts) |
| 465 return BUILDER_NAME_SEP.join(name_parts) |
| 425 | 466 |
| 426 | 467 |
| 427 def MakeCompileBuilderName(builder_base_name, release=False): | 468 def _MakeBuilder(helper, role, os, model, gpu, configuration, arch, |
| 428 if release: | 469 gm_image_subdir, factory_type, extra_config=None, |
| 429 compile_name = 'Compile_Release' | 470 perf_output_basedir=None, extra_branches=None, is_trybot=False, |
| 430 else: | 471 **kwargs): |
| 431 compile_name = 'Compile_Debug' | 472 """ Creates a builder and scheduler. """ |
| 432 return MakeBuilderName(builder_base_name, compile_name) | |
| 433 | |
| 434 | |
| 435 def MakeDebugBuilderName(builder_base_name): | |
| 436 return MakeBuilderName(builder_base_name, 'Debug') | |
| 437 | |
| 438 | |
| 439 def MakeReleaseBuilderName(builder_base_name): | |
| 440 return MakeBuilderName(builder_base_name, 'Release') | |
| 441 | |
| 442 | |
| 443 def MakeBenchBuilderName(builder_base_name): | |
| 444 return MakeBuilderName(builder_base_name, 'Bench') | |
| 445 | |
| 446 | |
| 447 def MakeSchedulerName(builder_base_name): | |
| 448 return MakeBuilderName(builder_base_name, 'Scheduler') | |
| 449 | |
| 450 | |
| 451 def _MakeBuilderSet(helper, builder_base_name, gm_image_subdir, factory_type, | |
| 452 perf_output_basedir=None, extra_branches=None, | |
| 453 do_compile=True, do_debug=True, do_release=True, | |
| 454 do_bench=True, try_schedulers=None, | |
| 455 compile_bot_warnings_as_errors=True, **kwargs): | |
| 456 """ Creates a trio of builders for a given platform: | |
| 457 1. Debug mode builder which runs all steps | |
| 458 2. Release mode builder which runs all steps EXCEPT benchmarks | |
| 459 3. Release mode builder which runs ONLY benchmarks. | |
| 460 """ | |
| 461 B = helper.Builder | 473 B = helper.Builder |
| 462 F = helper.Factory | 474 F = helper.Factory |
| 463 | 475 |
| 464 if not extra_branches: | 476 if not extra_branches: |
| 465 extra_branches = [] | 477 extra_branches = [] |
| 466 subdirs_to_checkout = set(extra_branches) | 478 subdirs_to_checkout = set(extra_branches) |
| 467 if gm_image_subdir: | 479 if gm_image_subdir: |
| 468 gm_image_branch = 'gm-expected/%s' % gm_image_subdir | 480 gm_image_branch = 'gm-expected/%s' % gm_image_subdir |
| 469 subdirs_to_checkout.add(gm_image_branch) | 481 subdirs_to_checkout.add(gm_image_branch) |
| 470 | 482 |
| 471 if try_schedulers: | 483 builder_name = MakeBuilderName( |
| 472 scheduler_name = '|'.join(try_schedulers) | 484 role=role, |
| 473 builder_base_name = builder_base_name + TRYBOT_NAME_SUFFIX | 485 os=os, |
| 486 model=model, |
| 487 gpu=gpu, |
| 488 configuration=configuration, |
| 489 arch=arch, |
| 490 extra_config=extra_config, |
| 491 is_trybot=is_trybot) |
| 492 |
| 493 if is_trybot: |
| 494 scheduler_name = TRY_SCHEDULERS_STR |
| 474 else: | 495 else: |
| 475 scheduler_name = MakeSchedulerName(builder_base_name) | 496 scheduler_name = builder_name + BUILDER_NAME_SEP + 'Scheduler' |
| 476 branches = list(subdirs_to_checkout.union(SKIA_PRIMARY_SUBDIRS)) | 497 branches = list(subdirs_to_checkout.union(SKIA_PRIMARY_SUBDIRS)) |
| 477 helper.AnyBranchScheduler(scheduler_name, branches=branches) | 498 helper.AnyBranchScheduler(scheduler_name, branches=branches) |
| 478 | 499 |
| 479 if do_compile: | 500 B(builder_name, 'f_%s' % builder_name, scheduler=scheduler_name) |
| 480 compile_debug_builder_name = MakeCompileBuilderName(builder_base_name, | 501 F('f_%s' % builder_name, factory_type( |
| 481 release=False) | 502 builder_name=builder_name, |
| 482 B(compile_debug_builder_name, 'f_%s' % compile_debug_builder_name, | 503 other_subdirs=subdirs_to_checkout, |
| 483 # Do not add gatekeeper for trybots. | 504 configuration=configuration, |
| 484 gatekeeper='GateKeeper' if try_schedulers is None else None, | 505 gm_image_subdir=gm_image_subdir, |
| 485 scheduler=scheduler_name, override_category=CATEGORY_BUILD) | 506 do_patch_step=is_trybot, |
| 486 F('f_%s' % compile_debug_builder_name, factory_type( | 507 perf_output_basedir=perf_output_basedir, |
| 487 builder_name=compile_debug_builder_name, | 508 **kwargs |
| 488 other_subdirs=subdirs_to_checkout, | 509 ).Build(role=role)) |
| 489 configuration='Debug', | |
| 490 gm_image_subdir=gm_image_subdir, | |
| 491 do_patch_step=(try_schedulers is not None), | |
| 492 perf_output_basedir=None, | |
| 493 compile_warnings_as_errors=compile_bot_warnings_as_errors, | |
| 494 **kwargs | |
| 495 ).BuildCompileOnly()) | |
| 496 compile_release_builder_name = MakeCompileBuilderName(builder_base_name, | |
| 497 release=True) | |
| 498 B(compile_release_builder_name, 'f_%s' % compile_release_builder_name, | |
| 499 # Do not add gatekeeper for trybots. | |
| 500 gatekeeper='GateKeeper' if try_schedulers is None else None, | |
| 501 scheduler=scheduler_name, override_category=CATEGORY_BUILD) | |
| 502 F('f_%s' % compile_release_builder_name, factory_type( | |
| 503 builder_name=compile_release_builder_name, | |
| 504 other_subdirs=subdirs_to_checkout, | |
| 505 configuration='Release', | |
| 506 gm_image_subdir=gm_image_subdir, | |
| 507 do_patch_step=(try_schedulers is not None), | |
| 508 perf_output_basedir=None, | |
| 509 compile_warnings_as_errors=compile_bot_warnings_as_errors, | |
| 510 **kwargs | |
| 511 ).BuildCompileOnly()) | |
| 512 | |
| 513 if do_debug: | |
| 514 debug_builder_name = MakeDebugBuilderName(builder_base_name) | |
| 515 B(debug_builder_name, 'f_%s' % debug_builder_name, | |
| 516 scheduler=scheduler_name) | |
| 517 F('f_%s' % debug_builder_name, factory_type( | |
| 518 builder_name=debug_builder_name, | |
| 519 other_subdirs=subdirs_to_checkout, | |
| 520 configuration='Debug', | |
| 521 gm_image_subdir=gm_image_subdir, | |
| 522 do_patch_step=(try_schedulers is not None), | |
| 523 perf_output_basedir=None, | |
| 524 compile_warnings_as_errors=False, | |
| 525 **kwargs | |
| 526 ).Build()) | |
| 527 | |
| 528 if do_release: | |
| 529 no_perf_builder_name = MakeReleaseBuilderName(builder_base_name) | |
| 530 B(no_perf_builder_name, 'f_%s' % no_perf_builder_name, | |
| 531 scheduler=scheduler_name) | |
| 532 F('f_%s' % no_perf_builder_name, factory_type( | |
| 533 builder_name=no_perf_builder_name, | |
| 534 other_subdirs=subdirs_to_checkout, | |
| 535 configuration='Release', | |
| 536 gm_image_subdir=gm_image_subdir, | |
| 537 do_patch_step=(try_schedulers is not None), | |
| 538 perf_output_basedir=None, | |
| 539 compile_warnings_as_errors=False, | |
| 540 **kwargs | |
| 541 ).BuildNoPerf()) | |
| 542 | |
| 543 if do_bench: | |
| 544 perf_builder_name = MakeBenchBuilderName(builder_base_name) | |
| 545 B(perf_builder_name, 'f_%s' % perf_builder_name, | |
| 546 scheduler=scheduler_name) | |
| 547 F('f_%s' % perf_builder_name, factory_type( | |
| 548 builder_name=perf_builder_name, | |
| 549 other_subdirs=subdirs_to_checkout, | |
| 550 configuration='Release', | |
| 551 gm_image_subdir=gm_image_subdir, | |
| 552 do_patch_step=(try_schedulers is not None), | |
| 553 perf_output_basedir=perf_output_basedir, | |
| 554 compile_warnings_as_errors=False, | |
| 555 **kwargs | |
| 556 ).BuildPerfOnly()) | |
| 557 | 510 |
| 558 | 511 |
| 559 def _MakeBuilderAndMaybeTrybotSet(do_trybots=True, **kwargs): | 512 def _MakeBuilderAndMaybeTrybotSet(do_trybots=True, **kwargs): |
| 560 _MakeBuilderSet(try_schedulers=None, **kwargs) | 513 _MakeBuilder(**kwargs) |
| 561 if do_trybots: | 514 if do_trybots: |
| 562 _MakeBuilderSet(try_schedulers=TRY_SCHEDULERS, **kwargs) | 515 _MakeBuilder(is_trybot=True, **kwargs) |
| 563 | 516 |
| 564 | 517 |
| 565 def MakeBuilderSet(**kwargs): | 518 def MakeBuilderSet(**kwargs): |
| 566 _MakeBuilderAndMaybeTrybotSet(**kwargs) | 519 _MakeBuilderAndMaybeTrybotSet(**kwargs) |
| 567 | 520 |
| 568 | 521 |
| 569 def MakeHousekeeperBuilderSet(helper, percommit_factory_type, | 522 def _MakeCompileBuilder(helper, scheduler, os, compiler, configuration, |
| 570 periodic_factory_type, do_trybots, **kwargs): | 523 target_arch, factory_type, is_trybot, |
| 571 B = helper.Builder | 524 extra_config=None, **kwargs): |
| 572 F = helper.Factory | 525 builder_name = MakeBuilderName(role=BUILDER_ROLE_COMPILE, |
| 526 os=os, |
| 527 compiler=compiler, |
| 528 configuration=configuration, |
| 529 target_arch=target_arch, |
| 530 extra_config=extra_config, |
| 531 is_trybot=is_trybot) |
| 532 helper.Builder(builder_name, 'f_%s' % builder_name, |
| 533 # Do not add gatekeeper for trybots. |
| 534 gatekeeper='GateKeeper' if is_trybot else None, |
| 535 scheduler=scheduler) |
| 536 helper.Factory('f_%s' % builder_name, factory_type( |
| 537 builder_name=builder_name, |
| 538 do_patch_step=is_trybot, |
| 539 configuration=configuration, |
| 540 **kwargs |
| 541 ).Build(role=BUILDER_ROLE_COMPILE)) |
| 542 return builder_name |
| 573 | 543 |
| 574 builder_factory_scheduler = [ | 544 |
| 575 # The Percommit housekeeper | 545 def MakeCompileBuilderSet(scheduler, do_trybots=True, **kwargs): |
| 576 ('Skia_PerCommit_House_Keeping', | |
| 577 percommit_factory_type, | |
| 578 'skia_rel'), | |
| 579 # The Periodic housekeeper | |
| 580 ('Skia_Periodic_House_Keeping', | |
| 581 periodic_factory_type, | |
| 582 'skia_periodic'), | |
| 583 ] | |
| 584 if do_trybots: | 546 if do_trybots: |
| 585 # Add the corresponding trybot builders to the above list. | 547 _MakeCompileBuilder(scheduler=scheduler, is_trybot=True, **kwargs) |
| 586 builder_factory_scheduler.extend([ | 548 _MakeCompileBuilder(scheduler=TRY_SCHEDULERS_STR, is_trybot=False, **kwargs) |
| 587 (builder + TRYBOT_NAME_SUFFIX, factory, TRY_SCHEDULERS_STR) | |
| 588 for (builder, factory, _scheduler) in builder_factory_scheduler]) | |
| 589 | 549 |
| 590 for (builder_name, factory, scheduler) in builder_factory_scheduler: | |
| 591 B(builder_name, 'f_%s' % builder_name, scheduler=scheduler) | |
| 592 F('f_%s' % builder_name, | |
| 593 factory( | |
| 594 builder_name=builder_name, | |
| 595 do_patch_step=(scheduler == TRY_SCHEDULERS_STR), | |
| 596 **kwargs | |
| 597 ).Build()) | |
| 598 | 550 |
| 599 def CanMergeBuildRequests(req1, req2): | 551 def CanMergeBuildRequests(req1, req2): |
| 600 """ Determine whether or not two BuildRequests can be merged. Note that the | 552 """ Determine whether or not two BuildRequests can be merged. Note that the |
| 601 call to buildbot.sourcestamp.SourceStamp.canBeMergedWith() is conspicuously | 553 call to buildbot.sourcestamp.SourceStamp.canBeMergedWith() is conspicuously |
| 602 missing. This is because that method verifies that: | 554 missing. This is because that method verifies that: |
| 603 1. req1.source.repository == req2.source.repository | 555 1. req1.source.repository == req2.source.repository |
| 604 2. req1.source.project == req2.source.project | 556 2. req1.source.project == req2.source.project |
| 605 3. req1.source.branch == req2.source.branch | 557 3. req1.source.branch == req2.source.branch |
| 606 4. req1.patch == None and req2.patch = None | 558 4. req1.patch == None and req2.patch = None |
| 607 5. (req1.source.changes and req2.source.changes) or \ | 559 5. (req1.source.changes and req2.source.changes) or \ |
| (...skipping 28 matching lines...) Expand all Loading... |
| 636 # request is associated with a change but the revisions match (#5 above). | 588 # request is associated with a change but the revisions match (#5 above). |
| 637 if req1.source.changes and not req2.source.changes: | 589 if req1.source.changes and not req2.source.changes: |
| 638 return False | 590 return False |
| 639 if not req1.source.changes and req2.source.changes: | 591 if not req1.source.changes and req2.source.changes: |
| 640 return False | 592 return False |
| 641 if not (req1.source.changes and req2.source.changes): | 593 if not (req1.source.changes and req2.source.changes): |
| 642 if req1.source.revision != req2.source.revision: | 594 if req1.source.revision != req2.source.revision: |
| 643 return False | 595 return False |
| 644 | 596 |
| 645 return True | 597 return True |
| OLD | NEW |