| #!/usr/bin/env python |
| # Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| # |
| # Use of this source code is governed by a BSD-style license |
| # that can be found in the LICENSE file in the root of the source |
| # tree. An additional intellectual property rights grant can be found |
| # in the file PATENTS. All contributing project authors may |
| # be found in the AUTHORS file in the root of the source tree. |
| |
| __author__ = 'ivinnichenko@webrtc.org (Illya Vinnichenko)' |
| |
| import buildbot |
| import os |
| import sys |
| import urlparse |
| from buildbot.process import factory |
| from buildbot.process import properties |
| from buildbot.process.properties import WithProperties |
| from buildbot.steps.shell import ShellCommand |
| |
| # Defines the order of the booleans of the supported platforms in the test |
| # dictionaries in master.cfg. |
| SUPPORTED_PLATFORMS = ('Linux', 'Mac', 'Windows') |
| |
| SVN_LOCATION = 'http://webrtc.googlecode.com/svn/trunk' |
| VALGRIND_CMD = ['tools/valgrind-webrtc/webrtc_tests.sh', '-t', 'cmdline'] |
| |
| DEFAULT_COVERAGE_DIR = '/var/www/coverage/' |
| DEFAULT_MASTER_WORK_DIR = '.' |
| GCLIENT_RETRIES = 3 |
| |
| # Copied from trunk/tools/build/scripts/master/factory/chromium_factory.py |
| # but converted to a list since we set defines instead of using an environment |
| # variable. |
| # |
| # On valgrind bots, override the optimizer settings so we don't inline too |
| # much and make the stacks harder to figure out. Use the same settings |
| # on all buildbot masters to make it easier to move bots. |
| MEMORY_TOOLS_GYP_DEFINES = [ |
| # GCC flags |
| 'mac_debug_optimization=1 ', |
| 'mac_release_optimization=1 ', |
| 'release_optimize=1 ', |
| 'no_gc_sections=1 ', |
| 'debug_extra_cflags="-g -fno-inline -fno-omit-frame-pointer ' |
| '-fno-builtin -fno-optimize-sibling-calls" ', |
| 'release_extra_cflags="-g -fno-inline -fno-omit-frame-pointer ' |
| '-fno-builtin -fno-optimize-sibling-calls" ', |
| # MSVS flags |
| 'win_debug_RuntimeChecks=0 ', |
| 'win_debug_disable_iterator_debugging=1 ', |
| 'win_debug_Optimization=1 ', |
| 'win_debug_InlineFunctionExpansion=0 ', |
| 'win_release_InlineFunctionExpansion=0 ', |
| 'win_release_OmitFramePointers=0 ', |
| |
| 'linux_use_tcmalloc=1 ', |
| 'release_valgrind_build=1 ', |
| 'werror= ', |
| ] |
| |
| |
| class WebRTCFactory(factory.BuildFactory): |
| """Abstract superclass for all build factories. |
| |
| A build factory defines a sequence of steps to take in a build process. |
| This class provides some helper methods and some abstract methods that |
| can be overridden to create customized build sequences. |
| """ |
| |
| def __init__(self, build_status_oracle): |
| """Creates the abstract factory. |
| |
| Args: |
| build_status_oracle: An instance of BuildStatusOracle which is used to |
| keep track of our build state. |
| """ |
| factory.BuildFactory.__init__(self) |
| |
| self.build_status_oracle = build_status_oracle |
| self.properties = properties.Properties() |
| self.gyp_params = [] |
| self.release = False |
| |
| def EnableBuild(self): |
| """Adds steps for building WebRTC [must be overridden]. |
| |
| Implementations of this method must add clean and build steps so that |
| when all steps have been run, we have an up-to-date, complete and correct |
| build of WebRTC for the platform. It is up to the method how to do this. |
| """ |
| pass |
| |
| def EnableTests(self, tests): |
| """Adds test run steps for all tests in the list. |
| |
| This method must be run after enabling the build. |
| |
| Args: |
| tests: list of test to be run. |
| """ |
| for test in tests: |
| self.EnableTest(test) |
| |
| def AddCommonStep(self, cmd, descriptor='', workdir='build', |
| number_of_retries=0, halt_build_on_failure=True, |
| warn_on_failure=False): |
| """Adds a step which will run as a shell command on the slave. |
| |
| NOTE: you are recommended to use this method to add new shell commands |
| instead of the base-class addStep method, since steps added here will |
| work with the smart-clean system (e.g. only do a full rebuild if the |
| previous build failed). Steps handled outside this method will not lead |
| to a full rebuild on the next build if they fail. |
| |
| Args: |
| cmd: The command to run. This command follows the contract for |
| ShellCommand, so see that documentation for more details. |
| descriptor: A string, or a list of strings, describing what the step |
| does. The descriptor gets printed in the waterfall display. |
| workdir: The working directory to run the command in. |
| number_of_retries: Number of times to retry the command, if it fails. |
| halt_build_on_failure: Stops the build dead in its tracks if this step |
| fails. Use for critical steps. This option does not make sense with |
| warn_on_failure. |
| warn_on_failure: If true, this step isn't that important and will not |
| cause a failed build on failure. |
| """ |
| flunk_on_failure = not warn_on_failure |
| |
| if type(descriptor) is str: |
| descriptor = [descriptor] |
| # Add spaces to wrap long test names to make waterfall output more compact. |
| wrapped_text = self._WrapLongLines(descriptor) |
| |
| self.addStep(MonitoredRetryingShellCommand( |
| build_status_oracle=self.build_status_oracle, |
| number_of_retries=number_of_retries, |
| command=cmd, |
| workdir=workdir, |
| description=wrapped_text + ['running...'], |
| descriptionDone=wrapped_text, |
| warnOnFailure=warn_on_failure, |
| flunkOnFailure=flunk_on_failure, |
| haltOnFailure=halt_build_on_failure, |
| name='_'.join(descriptor))) |
| |
| def AddSmartCleanStep(self): |
| """Adds a smart clean step. |
| |
| Smart clean only cleans the whole repository if the build status oracle |
| thinks the last build failed. Otherwise it cleans just the build output. |
| """ |
| self.addStep(SmartClean(self.build_status_oracle)) |
| |
| def AddCommonTestRunStep(self, test, descriptor='', cmd=None, |
| workdir='build/trunk'): |
| """Adds a step for running a single test [must be overridden]. |
| |
| In general, failing tests should not halt the build and allow other tests |
| to execute. A failing test should fail, or 'flunk', the build though. |
| |
| Implementations of this method must add new steps through AddCommonStep |
| and not by calling addStep. |
| |
| Args: |
| test: The test binary name. The step will attempt to execute this |
| binary in the binary output folder, except if the cmd argument is |
| defined (in that case, we will run cmd instead and just use the |
| test name in the descriptor). |
| descriptor: This should either be a string or a list of strings. The |
| descriptor or descriptors are appended to the test name and |
| displayed in the waterfall. |
| cmd: If necessary, you can specify this argument to override the |
| default behavior, which is to just run the binary specified in |
| test without arguments. |
| workdir: The base working directory to run the command in. This |
| directory will map to the WebRTC project root, e.g. the trunk |
| directory. This method will make sure that the test binary is run |
| in the correct output directory for the platform. |
| """ |
| pass |
| |
| def EnableTest(self, test): |
| """Makes a test run in the build sequence. May be overridden. |
| |
| Override to handle special cases for specific platforms, for instance if |
| a particular test binary requires command line arguments. |
| |
| Args: |
| test: The test name to enable. |
| """ |
| self.AddCommonTestRunStep(test) |
| |
| def AddGclientSyncStep(self, force_sync): |
| """Helper method for invoking gclient sync. Will retry if the operation |
| fails. |
| |
| Args: |
| force_sync: If the sync should be forced, i.e. update even for |
| unchanged modules (known to be required for Windows sometimes). |
| """ |
| cmd = ['gclient', 'sync'] |
| if force_sync: |
| cmd.append('--force') |
| self.AddCommonStep(cmd, descriptor='Sync', |
| number_of_retries=GCLIENT_RETRIES) |
| |
| def AddCommonGYPStep(self, gyp_file, gyp_params=[], descriptor='gyp'): |
| """Helper method for invoking GYP on WebRTC. |
| |
| GYP will generate makefiles or its equivalent in a platform-specific |
| manner. A failed GYP step will halt the build. |
| |
| Implementations of this method must add new steps through AddCommonStep |
| and not by calling addStep. |
| |
| Args: |
| gyp_file: The root GYP file to use. |
| gyp_params: Custom GYP parameters (same semantics as the GYP_PARAMS |
| environment variable). |
| descriptor: The descriptor to use for the step. |
| """ |
| cmd = ['./build/gyp_chromium', '--depth=.', gyp_file] |
| cmd += gyp_params + self.gyp_params |
| self.AddCommonStep(cmd=cmd, workdir='build/trunk', descriptor=descriptor) |
| |
| def _WrapLongLines(self, string_list, max_line_length=25, wrap_character='_'): |
| """ Creates a list with wrapped strings for lines that are too long. |
| |
| This is done by inserting spaces to long lines with the wrap character |
| in. It's a simple way to make long test targets wrap nicer in the |
| waterfall display. |
| |
| This method should only be used for lists that are displayed in the web |
| interface! |
| |
| Args: |
| string_list: List of strings where each string represents one line. |
| max_line_length: Number of characters a line may have to avoid |
| getting wrapped. |
| wrap_character: The character we're looking for when inserting a |
| space if a string is larger than max_line_length. If no such |
| character is found, no space will be inserted. |
| Returns: |
| A new list of the same length as the input list, but with strings |
| that may contain extra spaces in them, if longer than the max |
| length. |
| """ |
| result = [] |
| for line in string_list: |
| if len(line) > max_line_length: |
| index = line.rfind(wrap_character) |
| if index != -1: |
| line = line[:index] + ' ' + line[index:] |
| result.append(line) |
| return result |
| |
| |
| class BuildStatusOracle: |
| """Keeps track of a particular build's state. |
| |
| The oracle uses files in the default master work directory to keep track |
| of whether a build has failed. It only keeps track of the most recent build |
| until told to forget it. |
| """ |
| |
| def __init__(self, builder_name): |
| """Creates the oracle. |
| |
| Args: |
| builder_name: The name of the associated builder. This name is used |
| in the filename on disk. This name should be unique. |
| """ |
| self.builder_name = builder_name |
| self.master_work_dir = DEFAULT_MASTER_WORK_DIR |
| |
| def LastBuildFailed(self): |
| failure_file_path = self._GetFailureBuildPath() |
| return os.path.exists(failure_file_path) |
| |
| def ForgetLastBuild(self): |
| if self.LastBuildFailed(): |
| os.remove(self._GetFailureBuildPath()) |
| |
| def SetLastBuildAsFailed(self): |
| open(self._GetFailureBuildPath(), 'w').close() |
| |
| def _GetFailureBuildPath(self): |
| return os.path.join(self.master_work_dir, self.builder_name + ".failed") |
| |
| |
| class MonitoredRetryingShellCommand(ShellCommand): |
| """Wraps a shell command and notifies the oracle if the command fails. |
| |
| If the command fails, there's an option to retry it a number of times. |
| Default behavior is to not retry.""" |
| |
| def __init__(self, build_status_oracle, number_of_retries=0, **kwargs): |
| ShellCommand.__init__(self, **kwargs) |
| |
| self.addFactoryArguments(build_status_oracle=build_status_oracle, |
| number_of_retries=number_of_retries) |
| self.build_status_oracle = build_status_oracle |
| self.number_of_retries = number_of_retries |
| |
| def finished(self, results): |
| if (results == buildbot.status.builder.FAILURE or |
| results == buildbot.status.builder.EXCEPTION): |
| if self.number_of_retries > 0: |
| self.number_of_retries -= 1 |
| self.start() |
| return |
| else: |
| self.build_status_oracle.SetLastBuildAsFailed() |
| ShellCommand.finished(self, results) |
| |
| |
| class SmartClean(ShellCommand): |
| """Cleans the repository fully or partially depending on the build state.""" |
| def __init__(self, build_status_oracle, **kwargs): |
| ShellCommand.__init__(self, **kwargs) |
| |
| self.addFactoryArguments(build_status_oracle=build_status_oracle) |
| self.haltOnFailure = True |
| self.build_status_oracle = build_status_oracle |
| |
| def start(self): |
| if self.build_status_oracle.LastBuildFailed(): |
| self.build_status_oracle.ForgetLastBuild() |
| self.description = ['Nuke Repository', '(Previous Failed)'] |
| self.setCommand(['rm', '-rf', 'trunk']) |
| else: |
| self.description = ['Clean'] |
| self.setCommand('rm -rf trunk/out && ' |
| 'rm -rf trunk/xcodebuild &&' |
| 'rm -rf trunk/build/Debug &&' |
| 'rm -rf trunk/build/Release') |
| ShellCommand.start(self) |
| |
| |
| class GenerateCodeCoverage(ShellCommand): |
| """This custom shell command generates coverage HTML using genhtml. |
| |
| The command will dump the HTML output into coverage_dir, in a directory |
| whose name is generated from the build number and slave name. We will |
| expect that the coverage directory is somewhere under the web server root |
| (i.e. public html root) that corresponds to the web server URL. That is, if |
| we write Foo to the coverage directory we expect that directory to be |
| reachable from url/Foo. |
| """ |
| |
| def __init__(self, coverage_url, coverage_dir, coverage_file, **kwargs): |
| """Prepares the coverage command. |
| |
| Args: |
| coverage_url: The base URL for the serving web server we will use |
| when we generate the link to the coverage. This will generally |
| be the slave's URL (something like http://slave-hostname/). |
| coverage_dir: Where to write coverage HTML. |
| coverage_file: The LCOV file to generate the coverage from. |
| """ |
| ShellCommand.__init__(self, **kwargs) |
| self.addFactoryArguments(coverage_url=coverage_url, |
| coverage_dir=coverage_dir, |
| coverage_file=coverage_file) |
| self.setDefaultWorkdir('build/trunk') |
| self.coverage_url = coverage_url |
| self.coverage_dir = coverage_dir |
| self.coverage_file = coverage_file |
| self.description = ['Coverage Report'] |
| self.warnOnFailure = True |
| self.flunkOnFailure = False |
| output_dir = os.path.join(coverage_dir, |
| '%(buildername)s_%(buildnumber)s') |
| self.setCommand(['./tools/continuous_build/generate_coverage_html.sh', |
| coverage_file, WithProperties(output_dir)]) |
| |
| def createSummary(self, log): |
| coverage_url = urlparse.urljoin(self.coverage_url, |
| '%s_%s' % (self.getProperty('buildername'), |
| self.getProperty('buildnumber'))) |
| self.addURL('click here', coverage_url) |
| |
| def start(self): |
| ShellCommand.start(self) |
| |
| |
| class WebRTCAndroidFactory(WebRTCFactory): |
| """Sets up the Android build.""" |
| |
| def __init__(self, build_status_oracle): |
| WebRTCFactory.__init__(self, build_status_oracle) |
| |
| def EnableBuild(self, product='toro'): |
| prefix = 'rm -rf out/target/product/%s/obj/' % product |
| cleanup_list = [ |
| 'rm -rf external/webrtc', |
| prefix + 'STATIC_LIBRARIES/libwebrtc_*', |
| prefix + 'SHARE_LIBRARIES/libwebrtc_*', |
| prefix + 'EXECUTABLES/webrtc_*' |
| ] |
| cmd = ' ; '.join(cleanup_list) |
| self.AddCommonStep(cmd, descriptor='cleanup', workdir='build/trunk') |
| |
| cmd = 'svn checkout %s external/webrtc' % SVN_LOCATION |
| self.AddCommonStep(cmd, descriptor='svn (checkout)', workdir='build/trunk') |
| |
| cmd = ('source build/envsetup.sh && lunch full_%s-eng ' |
| '&& mmm external/webrtc showcommands' % product) |
| self.AddCommonStep(cmd, descriptor='build', workdir='build/trunk') |
| |
| |
| class WebRTCChromeFactory(WebRTCFactory): |
| """Sets up the Chrome OS build.""" |
| |
| def __init__(self, build_status_oracle): |
| WebRTCFactory.__init__(self, build_status_oracle) |
| |
| def EnableBuild(self): |
| self.AddCommonStep(['rm', '-rf', 'src'], descriptor='Cleanup') |
| self.AddGclientSyncStep(force_sync=True) |
| self.AddCommonMakeStep('chrome') |
| |
| def AddCommonMakeStep(self, target, make_extra=None): |
| descriptor = ['make ' + target] |
| cmd = ['make', target, '-j100'] |
| if make_extra is not None: |
| cmd.append(make_extra) |
| self.AddCommonStep(cmd=cmd, descriptor=descriptor, |
| warn_on_failure=True, workdir='build/src') |
| |
| |
| class WebRTCLinuxFactory(WebRTCFactory): |
| """Sets up the Linux build. |
| |
| This factory is quite configurable and can run a variety of builds. |
| """ |
| |
| def __init__(self, build_status_oracle, valgrind_enabled=False): |
| WebRTCFactory.__init__(self, build_status_oracle) |
| |
| self.build_enabled = False |
| self.coverage_enabled = False |
| self.valgrind_enabled = valgrind_enabled |
| |
| def EnableCoverage(self, coverage_url, coverage_dir=DEFAULT_COVERAGE_DIR): |
| """Enables coverage measurements using LCOV/GCOV. |
| |
| This method must be called before enabling build. |
| |
| Args: |
| coverage_url: See the GenerateCodeCoverage command's contract for |
| this argument. |
| coverage_dir: See the GenerateCodeCoverage command's contract for |
| this argument. |
| """ |
| assert self.build_enabled is False |
| |
| self.coverage_enabled = True |
| self.coverage_url = coverage_url |
| self.coverage_dir = coverage_dir |
| |
| def EnableBuild(self, release=False, build32=False, chrome_os=False, |
| clang=False): |
| if build32: |
| self.gyp_params.append('-Dtarget_arch=ia32') |
| |
| self.build_enabled = True |
| self.release = release |
| |
| self.AddSmartCleanStep() |
| |
| # Valgrind bots need special GYP defines to enable memory profiling |
| # friendly compilation. They already has a custom .gclient configuration |
| # file created so they don't need one being generated like the other bots. |
| if self.valgrind_enabled: |
| for gyp_define in MEMORY_TOOLS_GYP_DEFINES: |
| self.gyp_params.append('-D' + gyp_define) |
| else: |
| self.AddCommonStep(['gclient', 'config', SVN_LOCATION], |
| descriptor='gclient_config') |
| self.AddGclientSyncStep(force_sync=False) |
| |
| if chrome_os: |
| self.gyp_params.append('-Dchromeos=1') |
| |
| if clang: |
| self.gyp_params.append('-Dclang=1') |
| |
| if self.coverage_enabled: |
| self.gyp_params.append('-Dcoverage=1') |
| self.AddCommonGYPStep('webrtc.gyp', descriptor='CommonGYP') |
| |
| if clang: |
| self.AddCommonStep(['trunk/tools/clang/scripts/update.sh'], |
| descriptor='Update_Clang') |
| |
| if self.release: |
| self.AddCommonMakeStep('all', make_extra='BUILDTYPE=Release') |
| else: |
| self.AddCommonMakeStep('all') |
| |
| def AddCommonTestRunStep(self, test, extra_text=None, cmd=None, |
| workdir='build/trunk'): |
| descriptor = [test, extra_text] if extra_text else [test] |
| if cmd is None: |
| test_folder = 'Release' if self.release else 'Debug' |
| cmd = ['out/%s/%s' % (test_folder, test)] |
| if self.valgrind_enabled: |
| cmd = VALGRIND_CMD + cmd |
| self.AddCommonStep(cmd, descriptor=descriptor, workdir=workdir, |
| halt_build_on_failure=False) |
| |
| def AddXvfbTestRunStep(self, test_name, test_binary, test_arguments=''): |
| """ Adds a test to be run inside a XVFB window manager.""" |
| cmd = ('xvfb-run ' |
| '--server-args="-screen 0 800x600x24 -extension Composite" ' |
| '%s %s' % (test_binary, test_arguments)) |
| self.AddCommonTestRunStep(test=test_name, cmd=cmd) |
| |
| def AddCommonMakeStep(self, target, extra_text=None, make_extra=None): |
| descriptor = ['make ' + target, extra_text] if extra_text else ['make ' + |
| target] |
| cmd = ['make', target, '-j100'] |
| if make_extra: |
| cmd.append(make_extra) |
| self.AddCommonStep(cmd=cmd, descriptor=descriptor, workdir='build/trunk') |
| |
| def AddStepsToEstablishCoverageBaseline(self): |
| self.AddCommonStep(['lcov', '--directory', '.', '--capture', '-b', |
| '.', '--initial', |
| '--output-file', 'webrtc_base.info'], |
| workdir='build/trunk', |
| warn_on_failure=True, |
| halt_build_on_failure=False, |
| descriptor='LCOV (Baseline Capture)') |
| self.AddCommonStep(['lcov', '--extract', 'webrtc_base.info', '*/src/*', |
| '--output', 'filtered.info'], |
| workdir='build/trunk', |
| warn_on_failure=True, |
| halt_build_on_failure=False, |
| descriptor='LCOV (Baseline Extract)') |
| self.AddCommonStep(['lcov', '--remove', 'filtered.info', '*/usr/include/*', |
| '/third*', '/testing/*', '*/test/*', '*_unittest.*', |
| '*/mock/*', '--output', |
| 'webrtc_base_filtered_final.info'], |
| workdir='build/trunk', |
| warn_on_failure=True, |
| halt_build_on_failure=False, |
| descriptor='LCOV (Baseline Filter)') |
| |
| def AddStepsToComputeCoverage(self): |
| """Enable coverage data.""" |
| |
| # Delete all third-party .gcda files to save time and work around a bug |
| # in lcov which tends to hang when capturing on libjpgturbo. |
| self.AddCommonStep(['./tools/continuous_build/clean_third_party_gcda.sh'], |
| warn_on_failure=True, |
| halt_build_on_failure=False, |
| workdir='build/trunk', |
| descriptor='LCOV (Delete 3rd party)') |
| self.AddCommonStep(['lcov', '--directory', '.', '--capture', '-b', |
| '.', '--output-file', 'webrtc.info'], |
| warn_on_failure=True, |
| halt_build_on_failure=False, |
| workdir='build/trunk', |
| descriptor='LCOV (Capture)') |
| self.AddCommonStep(['lcov', '--extract', 'webrtc.info', '*/src/*', |
| '--output', 'test.info'], |
| warn_on_failure=True, |
| halt_build_on_failure=False, |
| workdir='build/trunk', |
| descriptor='LCOV (Extract)') |
| self.AddCommonStep(['lcov', '--remove', 'test.info', '*/usr/include/*', |
| '/third*', '/testing/*', '*/test/*', '*_unittest.*', |
| '*/mock/*', '--output', |
| 'final.info'], |
| warn_on_failure=True, |
| halt_build_on_failure=False, |
| workdir='build/trunk', |
| descriptor='LCOV (Filter)') |
| self.AddCommonStep(['lcov', '-a', 'webrtc_base_filtered_final.info', '-a', |
| 'final.info', '-o', 'final.info'], |
| warn_on_failure=True, |
| halt_build_on_failure=False, |
| workdir='build/trunk', |
| descriptor='LCOV (Merge)') |
| self.addStep(GenerateCodeCoverage(coverage_url=self.coverage_url, |
| coverage_dir=self.coverage_dir, |
| coverage_file='final.info')) |
| |
| def EnableTests(self, tests): |
| if self.coverage_enabled: |
| self.AddStepsToEstablishCoverageBaseline() |
| |
| WebRTCFactory.EnableTests(self, tests) |
| |
| if self.coverage_enabled: |
| self.AddStepsToComputeCoverage() |
| |
| def EnableTest(self, test): |
| """Adds a step for running a test on Linux. |
| |
| In general, this method will interpret the name as the name of a binary |
| in the default build output directory, except for a few special cases |
| which require custom command lines. |
| |
| Args: |
| test: the test name as a string. |
| """ |
| if test == 'audioproc_unittest': |
| self.AddCommonTestRunStep(test) |
| self.AddCommonGYPStep('webrtc.gyp', gyp_params=['-Dprefer_fixed_point=1'], |
| descriptor='GYP fixed point') |
| self.AddCommonMakeStep(test, extra_text='(fixed point)') |
| self.AddCommonTestRunStep(test, extra_text='(fixed point)') |
| elif test == 'vie_auto_test': |
| # TODO(phoglund): Enable the full stack test once it is completed and |
| # nonflaky. |
| binary = 'out/Debug/vie_auto_test' |
| args = ( |
| '--automated --gtest_filter="' |
| '-ViEVideoVerificationTest.RunsFullStackWithoutErrors" ' |
| '--capture_test_ensure_resolution_alignment_in_capture_device=false') |
| self.AddXvfbTestRunStep(test_name=test, test_binary=binary, |
| test_arguments=args) |
| elif test == 'video_render_module_test': |
| self.AddXvfbTestRunStep(test_name=test, |
| test_binary='out/Debug/video_render_module_test') |
| elif test == 'voe_auto_test': |
| cmd = 'out/Debug/voe_auto_test --automated' |
| self.AddCommonTestRunStep(test=test, cmd=cmd) |
| else: |
| self.AddCommonTestRunStep(test) |
| |
| |
| class WebRTCMacFactory(WebRTCFactory): |
| """Sets up the Mac build, both for make and xcode.""" |
| |
| def __init__(self, build_status_oracle): |
| WebRTCFactory.__init__(self, build_status_oracle) |
| self.build_type = 'both' |
| self.allowed_build_types = ['both', 'xcode', 'make'] |
| |
| def EnableBuild(self, build_type='both', release=False): |
| self.release = release |
| |
| if build_type not in self.allowed_build_types: |
| print '*** INCORRECT BUILD TYPE (%s)!!! ***' % build_type |
| sys.exit(0) |
| else: |
| self.build_type = build_type |
| self.AddSmartCleanStep() |
| self.AddCommonStep(['gclient', 'config', SVN_LOCATION], |
| descriptor='gclient_config') |
| self.AddGclientSyncStep(force_sync=True) |
| |
| if self.build_type == 'make' or self.build_type == 'both': |
| self.AddCommonGYPStep('webrtc.gyp', gyp_params=['-f', 'make'], |
| descriptor='EnableMake') |
| self.AddCommonMakeStep('all') |
| |
| def AddCommonTestRunStep(self, test, extra_text=None, cmd=None, |
| workdir='build/trunk'): |
| descriptor = [test, extra_text] if extra_text else [test] |
| if cmd is None: |
| out_path = 'xcodebuild' if self.build_type == 'xcode' else 'out' |
| test_folder = 'Release' if self.release else 'Debug' |
| cmd = ['%s/%s/%s' % (out_path, test_folder, test)] |
| |
| if self.build_type == 'xcode' or self.build_type == 'both': |
| self.AddCommonStep(cmd, descriptor=descriptor + ['(xcode)'], |
| halt_build_on_failure=False, workdir=workdir) |
| # Execute test only for 'make' build type. |
| # If 'both' is enabled we'll only execute the 'xcode' built ones. |
| if self.build_type == 'make': |
| self.AddCommonStep(cmd, descriptor=descriptor + ['(make)'], |
| halt_build_on_failure=False, workdir=workdir) |
| |
| def AddCommonMakeStep(self, target, extra_text=None, make_extra=None): |
| descriptor = [target, extra_text] if extra_text else [target] |
| if self.build_type == 'make' or self.build_type == 'both': |
| cmd = ['make', target, '-j100'] |
| if make_extra is not None: |
| cmd.append(make_extra) |
| if self.release: |
| cmd.append('BUILDTYPE=Release') |
| self.AddCommonStep(cmd, descriptor=descriptor + ['(make)'], |
| workdir='build/trunk') |
| if self.build_type == 'xcode' or self.build_type == 'both': |
| configuration = 'Release' if self.release else 'Debug' |
| cmd = ['xcodebuild', '-project', 'webrtc.xcodeproj', '-configuration', |
| configuration, '-target', 'All'] |
| self.AddCommonStep(cmd, descriptor=descriptor + ['(xcode)'], |
| workdir='build/trunk') |
| |
| class WebRTCWinFactory(WebRTCFactory): |
| """Sets up the Windows build. |
| |
| Allows building with Debug, Release or both in sequence. |
| """ |
| |
| def __init__(self, build_status_oracle): |
| WebRTCFactory.__init__(self, build_status_oracle) |
| self.configuration = 'Debug' |
| self.platform = 'x64' |
| self.allowed_platforms = ['x64', 'Win32'] |
| self.allowed_configurations = ['Debug', 'Release', 'both'] |
| |
| def EnableBuild(self, platform='Win32', configuration='Debug'): |
| if platform not in self.allowed_platforms: |
| print '*** INCORRECT PLATFORM (%s)!!! ***' % platform |
| sys.exit(0) |
| else: |
| self.platform = platform |
| if configuration not in self.allowed_configurations: |
| print '*** INCORRECT CONFIGURATION (%s)!!! ***' % configuration |
| sys.exit(0) |
| else: |
| self.configuration = configuration |
| |
| self.AddSmartCleanStep() |
| self.AddCommonStep(['gclient', 'config', SVN_LOCATION], |
| descriptor='gclient_config') |
| self.AddGclientSyncStep(force_sync=True) |
| |
| if self.configuration == 'Debug' or self.configuration == 'both': |
| cmd = ['msbuild', 'webrtc.sln', '/t:Clean', |
| '/p:Configuration=Debug;Platform=%s' % (self.platform)] |
| self.AddCommonStep(cmd, descriptor='Build(Clean)', workdir='build/trunk') |
| cmd = ['msbuild', 'webrtc.sln', |
| '/p:Configuration=Debug;Platform=%s' % (self.platform)] |
| self.AddCommonStep(cmd, descriptor='Build(Debug)', workdir='build/trunk') |
| if self.configuration == 'Release' or self.configuration == 'both': |
| cmd = ['msbuild', 'webrtc.sln', '/t:Clean', |
| '/p:Configuration=Release;Platform=%s' % (self.platform)] |
| self.AddCommonStep(cmd, descriptor='Build(Clean)', workdir='build/trunk') |
| cmd = ['msbuild', 'webrtc.sln', |
| '/p:Configuration=Release;Platform=%s' % (self.platform)] |
| self.AddCommonStep(cmd, descriptor='Build(Release)', |
| workdir='build/trunk') |
| |
| def AddCommonTestRunStep(self, test, cmd=None, workdir='build/trunk'): |
| descriptor = [test] |
| if self.configuration == 'Debug' or self.configuration == 'both': |
| if cmd is None: |
| cmd = ['build\Debug\%s.exe' % test] |
| self.AddCommonStep(cmd, descriptor=descriptor, |
| halt_build_on_failure=False, workdir=workdir) |
| if self.configuration == 'Release' or self.configuration == 'both': |
| if cmd is None: |
| cmd = ['build\Release\%s.exe' % test] |
| self.AddCommonStep(cmd, descriptor=descriptor, |
| halt_build_on_failure=False, workdir=workdir) |
| |
| # Utility functions |
| |
| |
| class UnsupportedPlatformError(Exception): |
| pass |
| |
| |
| def GetEnabledTests(test_dict, platform): |
| """Returns a list of enabled test names for the provided platform. |
| |
| Args: |
| test_dict: Dictionary mapping test names to tuples representing if the |
| test shall be enabled on each platform. Each tuple contains one |
| boolean for each platform. The platforms are in the order specified |
| by SUPPORTED_PLATFORMS. |
| platform: The platform we're looking to get the tests for. |
| |
| Returns: |
| A list of test names, sorted alphabetically. |
| |
| Raises: |
| UnsupportedPlatformError: if the platform supplied is not supported. |
| """ |
| if platform not in SUPPORTED_PLATFORMS: |
| raise UnsupportedPlatformError('*** UNSUPPORTED PLATFORM (%s)!!! ***' % |
| platform) |
| result = [] |
| platform_index = SUPPORTED_PLATFORMS.index(platform) |
| for test_name, enabled_platforms in test_dict.iteritems(): |
| if enabled_platforms[platform_index]: |
| result.append(test_name) |
| result.sort() |
| return result |