diff options
Diffstat (limited to 'deps/gyp/test/lib/TestGyp.py')
-rw-r--r-- | deps/gyp/test/lib/TestGyp.py | 1151 |
1 files changed, 0 insertions, 1151 deletions
diff --git a/deps/gyp/test/lib/TestGyp.py b/deps/gyp/test/lib/TestGyp.py deleted file mode 100644 index c59b811a8d..0000000000 --- a/deps/gyp/test/lib/TestGyp.py +++ /dev/null @@ -1,1151 +0,0 @@ -# Copyright (c) 2012 Google Inc. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -""" -TestGyp.py: a testing framework for GYP integration tests. -""" - -import collections -from contextlib import contextmanager -import itertools -import os -import re -import shutil -import subprocess -import sys -import tempfile - -import TestCmd -import TestCommon -from TestCommon import __all__ - -__all__.extend([ - 'TestGyp', -]) - - -def remove_debug_line_numbers(contents): - """Function to remove the line numbers from the debug output - of gyp and thus reduce the extreme fragility of the stdout - comparison tests. - """ - lines = contents.splitlines() - # split each line on ":" - lines = [l.split(":", 3) for l in lines] - # join each line back together while ignoring the - # 3rd column which is the line number - lines = [len(l) > 3 and ":".join(l[3:]) or l for l in lines] - return "\n".join(lines) - - -def match_modulo_line_numbers(contents_a, contents_b): - """File contents matcher that ignores line numbers.""" - contents_a = remove_debug_line_numbers(contents_a) - contents_b = remove_debug_line_numbers(contents_b) - return TestCommon.match_exact(contents_a, contents_b) - - -@contextmanager -def LocalEnv(local_env): - """Context manager to provide a local OS environment.""" - old_env = os.environ.copy() - os.environ.update(local_env) - try: - yield - finally: - os.environ.clear() - os.environ.update(old_env) - - -class TestGypBase(TestCommon.TestCommon): - """ - Class for controlling end-to-end tests of gyp generators. - - Instantiating this class will create a temporary directory and - arrange for its destruction (via the TestCmd superclass) and - copy all of the non-gyptest files in the directory hierarchy of the - executing script. - - The default behavior is to test the 'gyp' or 'gyp.bat' file in the - current directory. An alternative may be specified explicitly on - instantiation, or by setting the TESTGYP_GYP environment variable. - - This class should be subclassed for each supported gyp generator - (format). Various abstract methods below define calling signatures - used by the test scripts to invoke builds on the generated build - configuration and to run executables generated by those builds. - """ - - formats = [] - build_tool = None - build_tool_list = [] - - _exe = TestCommon.exe_suffix - _obj = TestCommon.obj_suffix - shobj_ = TestCommon.shobj_prefix - _shobj = TestCommon.shobj_suffix - lib_ = TestCommon.lib_prefix - _lib = TestCommon.lib_suffix - dll_ = TestCommon.dll_prefix - _dll = TestCommon.dll_suffix - module_ = TestCommon.module_prefix - _module = TestCommon.module_suffix - - # Constants to represent different targets. - ALL = '__all__' - DEFAULT = '__default__' - - # Constants for different target types. - EXECUTABLE = '__executable__' - STATIC_LIB = '__static_lib__' - SHARED_LIB = '__shared_lib__' - LOADABLE_MODULE = '__loadable_module__' - - def __init__(self, gyp=None, *args, **kw): - self.origin_cwd = os.path.abspath(os.path.dirname(sys.argv[0])) - self.extra_args = sys.argv[1:] - - if not gyp: - gyp = os.environ.get('TESTGYP_GYP') - if not gyp: - if sys.platform == 'win32': - gyp = 'gyp.bat' - else: - gyp = 'gyp' - self.gyp = os.path.abspath(gyp) - self.no_parallel = False - - self.formats = [self.format] - - self.initialize_build_tool() - - kw.setdefault('match', TestCommon.match_exact) - - # Put test output in out/testworkarea by default. - # Use temporary names so there are no collisions. - workdir = os.path.join('out', kw.get('workdir', 'testworkarea')) - # Create work area if it doesn't already exist. - if not os.path.isdir(workdir): - os.makedirs(workdir) - - kw['workdir'] = tempfile.mktemp(prefix='testgyp.', dir=workdir) - - formats = kw.pop('formats', []) - - super(TestGypBase, self).__init__(*args, **kw) - - real_format = self.format.split('-')[-1] - excluded_formats = set([f for f in formats if f[0] == '!']) - included_formats = set(formats) - excluded_formats - if ('!'+real_format in excluded_formats or - included_formats and real_format not in included_formats): - msg = 'Invalid test for %r format; skipping test.\n' - self.skip_test(msg % self.format) - - self.copy_test_configuration(self.origin_cwd, self.workdir) - self.set_configuration(None) - - # Set $HOME so that gyp doesn't read the user's actual - # ~/.gyp/include.gypi file, which may contain variables - # and other settings that would change the output. - os.environ['HOME'] = self.workpath() - # Clear $GYP_DEFINES for the same reason. - if 'GYP_DEFINES' in os.environ: - del os.environ['GYP_DEFINES'] - # Override the user's language settings, which could - # otherwise make the output vary from what is expected. - os.environ['LC_ALL'] = 'C' - - def built_file_must_exist(self, name, type=None, **kw): - """ - Fails the test if the specified built file name does not exist. - """ - return self.must_exist(self.built_file_path(name, type, **kw)) - - def built_file_must_not_exist(self, name, type=None, **kw): - """ - Fails the test if the specified built file name exists. - """ - return self.must_not_exist(self.built_file_path(name, type, **kw)) - - def built_file_must_match(self, name, contents, **kw): - """ - Fails the test if the contents of the specified built file name - do not match the specified contents. - """ - return self.must_match(self.built_file_path(name, **kw), contents) - - def built_file_must_not_match(self, name, contents, **kw): - """ - Fails the test if the contents of the specified built file name - match the specified contents. - """ - return self.must_not_match(self.built_file_path(name, **kw), contents) - - def built_file_must_not_contain(self, name, contents, **kw): - """ - Fails the test if the specified built file name contains the specified - contents. - """ - return self.must_not_contain(self.built_file_path(name, **kw), contents) - - def copy_test_configuration(self, source_dir, dest_dir): - """ - Copies the test configuration from the specified source_dir - (the directory in which the test script lives) to the - specified dest_dir (a temporary working directory). - - This ignores all files and directories that begin with - the string 'gyptest', and all '.svn' subdirectories. - """ - for root, dirs, files in os.walk(source_dir): - if '.svn' in dirs: - dirs.remove('.svn') - dirs = [ d for d in dirs if not d.startswith('gyptest') ] - files = [ f for f in files if not f.startswith('gyptest') ] - for dirname in dirs: - source = os.path.join(root, dirname) - destination = source.replace(source_dir, dest_dir) - os.mkdir(destination) - if sys.platform != 'win32': - shutil.copystat(source, destination) - for filename in files: - source = os.path.join(root, filename) - destination = source.replace(source_dir, dest_dir) - shutil.copy2(source, destination) - - def initialize_build_tool(self): - """ - Initializes the .build_tool attribute. - - Searches the .build_tool_list for an executable name on the user's - $PATH. The first tool on the list is used as-is if nothing is found - on the current $PATH. - """ - for build_tool in self.build_tool_list: - if not build_tool: - continue - if os.path.isabs(build_tool): - self.build_tool = build_tool - return - build_tool = self.where_is(build_tool) - if build_tool: - self.build_tool = build_tool - return - - if self.build_tool_list: - self.build_tool = self.build_tool_list[0] - - def relocate(self, source, destination): - """ - Renames (relocates) the specified source (usually a directory) - to the specified destination, creating the destination directory - first if necessary. - - Note: Don't use this as a generic "rename" operation. In the - future, "relocating" parts of a GYP tree may affect the state of - the test to modify the behavior of later method calls. - """ - destination_dir = os.path.dirname(destination) - if not os.path.exists(destination_dir): - self.subdir(destination_dir) - os.rename(source, destination) - - def report_not_up_to_date(self): - """ - Reports that a build is not up-to-date. - - This provides common reporting for formats that have complicated - conditions for checking whether a build is up-to-date. Formats - that expect exact output from the command (make) can - just set stdout= when they call the run_build() method. - """ - print "Build is not up-to-date:" - print self.banner('STDOUT ') - print self.stdout() - stderr = self.stderr() - if stderr: - print self.banner('STDERR ') - print stderr - - def run_gyp(self, gyp_file, *args, **kw): - """ - Runs gyp against the specified gyp_file with the specified args. - """ - - # When running gyp, and comparing its output we use a comparitor - # that ignores the line numbers that gyp logs in its debug output. - if kw.pop('ignore_line_numbers', False): - kw.setdefault('match', match_modulo_line_numbers) - - # TODO: --depth=. works around Chromium-specific tree climbing. - depth = kw.pop('depth', '.') - run_args = ['--depth='+depth] - run_args.extend(['--format='+f for f in self.formats]); - run_args.append(gyp_file) - if self.no_parallel: - run_args += ['--no-parallel'] - # TODO: if extra_args contains a '--build' flag - # we really want that to only apply to the last format (self.format). - run_args.extend(self.extra_args) - # Default xcode_ninja_target_pattern to ^.*$ to fix xcode-ninja tests - xcode_ninja_target_pattern = kw.pop('xcode_ninja_target_pattern', '.*') - run_args.extend( - ['-G', 'xcode_ninja_target_pattern=%s' % xcode_ninja_target_pattern]) - run_args.extend(args) - return self.run(program=self.gyp, arguments=run_args, **kw) - - def run(self, *args, **kw): - """ - Executes a program by calling the superclass .run() method. - - This exists to provide a common place to filter out keyword - arguments implemented in this layer, without having to update - the tool-specific subclasses or clutter the tests themselves - with platform-specific code. - """ - if kw.has_key('SYMROOT'): - del kw['SYMROOT'] - super(TestGypBase, self).run(*args, **kw) - - def set_configuration(self, configuration): - """ - Sets the configuration, to be used for invoking the build - tool and testing potential built output. - """ - self.configuration = configuration - - def configuration_dirname(self): - if self.configuration: - return self.configuration.split('|')[0] - else: - return 'Default' - - def configuration_buildname(self): - if self.configuration: - return self.configuration - else: - return 'Default' - - # - # Abstract methods to be defined by format-specific subclasses. - # - - def build(self, gyp_file, target=None, **kw): - """ - Runs a build of the specified target against the configuration - generated from the specified gyp_file. - - A 'target' argument of None or the special value TestGyp.DEFAULT - specifies the default argument for the underlying build tool. - A 'target' argument of TestGyp.ALL specifies the 'all' target - (if any) of the underlying build tool. - """ - raise NotImplementedError - - def built_file_path(self, name, type=None, **kw): - """ - Returns a path to the specified file name, of the specified type. - """ - raise NotImplementedError - - def built_file_basename(self, name, type=None, **kw): - """ - Returns the base name of the specified file name, of the specified type. - - A bare=True keyword argument specifies that prefixes and suffixes shouldn't - be applied. - """ - if not kw.get('bare'): - if type == self.EXECUTABLE: - name = name + self._exe - elif type == self.STATIC_LIB: - name = self.lib_ + name + self._lib - elif type == self.SHARED_LIB: - name = self.dll_ + name + self._dll - elif type == self.LOADABLE_MODULE: - name = self.module_ + name + self._module - return name - - def run_built_executable(self, name, *args, **kw): - """ - Runs an executable program built from a gyp-generated configuration. - - The specified name should be independent of any particular generator. - Subclasses should find the output executable in the appropriate - output build directory, tack on any necessary executable suffix, etc. - """ - raise NotImplementedError - - def up_to_date(self, gyp_file, target=None, **kw): - """ - Verifies that a build of the specified target is up to date. - - The subclass should implement this by calling build() - (or a reasonable equivalent), checking whatever conditions - will tell it the build was an "up to date" null build, and - failing if it isn't. - """ - raise NotImplementedError - - -class TestGypGypd(TestGypBase): - """ - Subclass for testing the GYP 'gypd' generator (spit out the - internal data structure as pretty-printed Python). - """ - format = 'gypd' - def __init__(self, gyp=None, *args, **kw): - super(TestGypGypd, self).__init__(*args, **kw) - # gypd implies the use of 'golden' files, so parallelizing conflicts as it - # causes ordering changes. - self.no_parallel = True - - -class TestGypCustom(TestGypBase): - """ - Subclass for testing the GYP with custom generator - """ - - def __init__(self, gyp=None, *args, **kw): - self.format = kw.pop("format") - super(TestGypCustom, self).__init__(*args, **kw) - - -class TestGypCMake(TestGypBase): - """ - Subclass for testing the GYP CMake generator, using cmake's ninja backend. - """ - format = 'cmake' - build_tool_list = ['cmake'] - ALL = 'all' - - def cmake_build(self, gyp_file, target=None, **kw): - arguments = kw.get('arguments', [])[:] - - self.build_tool_list = ['cmake'] - self.initialize_build_tool() - - chdir = os.path.join(kw.get('chdir', '.'), - 'out', - self.configuration_dirname()) - kw['chdir'] = chdir - - arguments.append('-G') - arguments.append('Ninja') - - kw['arguments'] = arguments - - stderr = kw.get('stderr', None) - if stderr: - kw['stderr'] = stderr.split('$$$')[0] - - self.run(program=self.build_tool, **kw) - - def ninja_build(self, gyp_file, target=None, **kw): - arguments = kw.get('arguments', [])[:] - - self.build_tool_list = ['ninja'] - self.initialize_build_tool() - - # Add a -C output/path to the command line. - arguments.append('-C') - arguments.append(os.path.join('out', self.configuration_dirname())) - - if target not in (None, self.DEFAULT): - arguments.append(target) - - kw['arguments'] = arguments - - stderr = kw.get('stderr', None) - if stderr: - stderrs = stderr.split('$$$') - kw['stderr'] = stderrs[1] if len(stderrs) > 1 else '' - - return self.run(program=self.build_tool, **kw) - - def build(self, gyp_file, target=None, status=0, **kw): - # Two tools must be run to build, cmake and the ninja. - # Allow cmake to succeed when the overall expectation is to fail. - if status is None: - kw['status'] = None - else: - if not isinstance(status, collections.Iterable): status = (status,) - kw['status'] = list(itertools.chain((0,), status)) - self.cmake_build(gyp_file, target, **kw) - kw['status'] = status - self.ninja_build(gyp_file, target, **kw) - - def run_built_executable(self, name, *args, **kw): - # Enclosing the name in a list avoids prepending the original dir. - program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] - if sys.platform == 'darwin': - configuration = self.configuration_dirname() - os.environ['DYLD_LIBRARY_PATH'] = os.path.join('out', configuration) - return self.run(program=program, *args, **kw) - - def built_file_path(self, name, type=None, **kw): - result = [] - chdir = kw.get('chdir') - if chdir: - result.append(chdir) - result.append('out') - result.append(self.configuration_dirname()) - if type == self.STATIC_LIB: - if sys.platform != 'darwin': - result.append('obj.target') - elif type == self.SHARED_LIB: - if sys.platform != 'darwin' and sys.platform != 'win32': - result.append('lib.target') - subdir = kw.get('subdir') - if subdir and type != self.SHARED_LIB: - result.append(subdir) - result.append(self.built_file_basename(name, type, **kw)) - return self.workpath(*result) - - def up_to_date(self, gyp_file, target=None, **kw): - result = self.ninja_build(gyp_file, target, **kw) - if not result: - stdout = self.stdout() - if 'ninja: no work to do' not in stdout: - self.report_not_up_to_date() - self.fail_test() - return result - - -class TestGypMake(TestGypBase): - """ - Subclass for testing the GYP Make generator. - """ - format = 'make' - build_tool_list = ['make'] - ALL = 'all' - def build(self, gyp_file, target=None, **kw): - """ - Runs a Make build using the Makefiles generated from the specified - gyp_file. - """ - arguments = kw.get('arguments', [])[:] - if self.configuration: - arguments.append('BUILDTYPE=' + self.configuration) - if target not in (None, self.DEFAULT): - arguments.append(target) - # Sub-directory builds provide per-gyp Makefiles (i.e. - # Makefile.gyp_filename), so use that if there is no Makefile. - chdir = kw.get('chdir', '') - if not os.path.exists(os.path.join(chdir, 'Makefile')): - print "NO Makefile in " + os.path.join(chdir, 'Makefile') - arguments.insert(0, '-f') - arguments.insert(1, os.path.splitext(gyp_file)[0] + '.Makefile') - kw['arguments'] = arguments - return self.run(program=self.build_tool, **kw) - def up_to_date(self, gyp_file, target=None, **kw): - """ - Verifies that a build of the specified Make target is up to date. - """ - if target in (None, self.DEFAULT): - message_target = 'all' - else: - message_target = target - kw['stdout'] = "make: Nothing to be done for `%s'.\n" % message_target - return self.build(gyp_file, target, **kw) - def run_built_executable(self, name, *args, **kw): - """ - Runs an executable built by Make. - """ - configuration = self.configuration_dirname() - libdir = os.path.join('out', configuration, 'lib') - # TODO(piman): when everything is cross-compile safe, remove lib.target - if sys.platform == 'darwin': - # Mac puts target shared libraries right in the product directory. - configuration = self.configuration_dirname() - os.environ['DYLD_LIBRARY_PATH'] = ( - libdir + '.host:' + os.path.join('out', configuration)) - else: - os.environ['LD_LIBRARY_PATH'] = libdir + '.host:' + libdir + '.target' - # Enclosing the name in a list avoids prepending the original dir. - program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] - return self.run(program=program, *args, **kw) - def built_file_path(self, name, type=None, **kw): - """ - Returns a path to the specified file name, of the specified type, - as built by Make. - - Built files are in the subdirectory 'out/{configuration}'. - The default is 'out/Default'. - - A chdir= keyword argument specifies the source directory - relative to which the output subdirectory can be found. - - "type" values of STATIC_LIB or SHARED_LIB append the necessary - prefixes and suffixes to a platform-independent library base name. - - A subdir= keyword argument specifies a library subdirectory within - the default 'obj.target'. - """ - result = [] - chdir = kw.get('chdir') - if chdir: - result.append(chdir) - configuration = self.configuration_dirname() - result.extend(['out', configuration]) - if type == self.STATIC_LIB and sys.platform != 'darwin': - result.append('obj.target') - elif type == self.SHARED_LIB and sys.platform != 'darwin': - result.append('lib.target') - subdir = kw.get('subdir') - if subdir and type != self.SHARED_LIB: - result.append(subdir) - result.append(self.built_file_basename(name, type, **kw)) - return self.workpath(*result) - - -def ConvertToCygpath(path): - """Convert to cygwin path if we are using cygwin.""" - if sys.platform == 'cygwin': - p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE) - path = p.communicate()[0].strip() - return path - - -def FindMSBuildInstallation(msvs_version = 'auto'): - """Returns path to MSBuild for msvs_version or latest available. - - Looks in the registry to find install location of MSBuild. - MSBuild before v4.0 will not build c++ projects, so only use newer versions. - """ - import TestWin - registry = TestWin.Registry() - - msvs_to_msbuild = { - '2013': r'12.0', - '2012': r'4.0', # Really v4.0.30319 which comes with .NET 4.5. - '2010': r'4.0'} - - msbuild_basekey = r'HKLM\SOFTWARE\Microsoft\MSBuild\ToolsVersions' - if not registry.KeyExists(msbuild_basekey): - print 'Error: could not find MSBuild base registry entry' - return None - - msbuild_version = None - if msvs_version in msvs_to_msbuild: - msbuild_test_version = msvs_to_msbuild[msvs_version] - if registry.KeyExists(msbuild_basekey + '\\' + msbuild_test_version): - msbuild_version = msbuild_test_version - else: - print ('Warning: Environment variable GYP_MSVS_VERSION specifies "%s" ' - 'but corresponding MSBuild "%s" was not found.' % - (msvs_version, msbuild_version)) - if not msbuild_version: - for msvs_version in sorted(msvs_to_msbuild, reverse=True): - msbuild_test_version = msvs_to_msbuild[msvs_version] - if registry.KeyExists(msbuild_basekey + '\\' + msbuild_test_version): - msbuild_version = msbuild_test_version - break - if not msbuild_version: - print 'Error: could not find MSBuild registry entry' - return None - - msbuild_path = registry.GetValue(msbuild_basekey + '\\' + msbuild_version, - 'MSBuildToolsPath') - if not msbuild_path: - print 'Error: could not get MSBuild registry entry value' - return None - - return os.path.join(msbuild_path, 'MSBuild.exe') - - -def FindVisualStudioInstallation(): - """Returns appropriate values for .build_tool and .uses_msbuild fields - of TestGypBase for Visual Studio. - - We use the value specified by GYP_MSVS_VERSION. If not specified, we - search %PATH% and %PATHEXT% for a devenv.{exe,bat,...} executable. - Failing that, we search for likely deployment paths. - """ - possible_roots = ['%s:\\Program Files%s' % (chr(drive), suffix) - for drive in range(ord('C'), ord('Z') + 1) - for suffix in ['', ' (x86)']] - possible_paths = { - '2013': r'Microsoft Visual Studio 12.0\Common7\IDE\devenv.com', - '2012': r'Microsoft Visual Studio 11.0\Common7\IDE\devenv.com', - '2010': r'Microsoft Visual Studio 10.0\Common7\IDE\devenv.com', - '2008': r'Microsoft Visual Studio 9.0\Common7\IDE\devenv.com', - '2005': r'Microsoft Visual Studio 8\Common7\IDE\devenv.com'} - - possible_roots = [ConvertToCygpath(r) for r in possible_roots] - - msvs_version = 'auto' - for flag in (f for f in sys.argv if f.startswith('msvs_version=')): - msvs_version = flag.split('=')[-1] - msvs_version = os.environ.get('GYP_MSVS_VERSION', msvs_version) - - if msvs_version in possible_paths: - # Check that the path to the specified GYP_MSVS_VERSION exists. - path = possible_paths[msvs_version] - for r in possible_roots: - build_tool = os.path.join(r, path) - if os.path.exists(build_tool): - uses_msbuild = msvs_version >= '2010' - msbuild_path = FindMSBuildInstallation(msvs_version) - return build_tool, uses_msbuild, msbuild_path - else: - print ('Warning: Environment variable GYP_MSVS_VERSION specifies "%s" ' - 'but corresponding "%s" was not found.' % (msvs_version, path)) - # Neither GYP_MSVS_VERSION nor the path help us out. Iterate through - # the choices looking for a match. - for version in sorted(possible_paths, reverse=True): - path = possible_paths[version] - for r in possible_roots: - build_tool = os.path.join(r, path) - if os.path.exists(build_tool): - uses_msbuild = msvs_version >= '2010' - msbuild_path = FindMSBuildInstallation(msvs_version) - return build_tool, uses_msbuild, msbuild_path - print 'Error: could not find devenv' - sys.exit(1) - -class TestGypOnMSToolchain(TestGypBase): - """ - Common subclass for testing generators that target the Microsoft Visual - Studio toolchain (cl, link, dumpbin, etc.) - """ - @staticmethod - def _ComputeVsvarsPath(devenv_path): - devenv_dir = os.path.split(devenv_path)[0] - vsvars_path = os.path.join(devenv_path, '../../Tools/vsvars32.bat') - return vsvars_path - - def initialize_build_tool(self): - super(TestGypOnMSToolchain, self).initialize_build_tool() - if sys.platform in ('win32', 'cygwin'): - build_tools = FindVisualStudioInstallation() - self.devenv_path, self.uses_msbuild, self.msbuild_path = build_tools - self.vsvars_path = TestGypOnMSToolchain._ComputeVsvarsPath( - self.devenv_path) - - def run_dumpbin(self, *dumpbin_args): - """Run the dumpbin tool with the specified arguments, and capturing and - returning stdout.""" - assert sys.platform in ('win32', 'cygwin') - cmd = os.environ.get('COMSPEC', 'cmd.exe') - arguments = [cmd, '/c', self.vsvars_path, '&&', 'dumpbin'] - arguments.extend(dumpbin_args) - proc = subprocess.Popen(arguments, stdout=subprocess.PIPE) - output = proc.communicate()[0] - assert not proc.returncode - return output - -class TestGypNinja(TestGypOnMSToolchain): - """ - Subclass for testing the GYP Ninja generator. - """ - format = 'ninja' - build_tool_list = ['ninja'] - ALL = 'all' - DEFAULT = 'all' - - def run_gyp(self, gyp_file, *args, **kw): - TestGypBase.run_gyp(self, gyp_file, *args, **kw) - - def build(self, gyp_file, target=None, **kw): - arguments = kw.get('arguments', [])[:] - - # Add a -C output/path to the command line. - arguments.append('-C') - arguments.append(os.path.join('out', self.configuration_dirname())) - - if target is None: - target = 'all' - arguments.append(target) - - kw['arguments'] = arguments - return self.run(program=self.build_tool, **kw) - - def run_built_executable(self, name, *args, **kw): - # Enclosing the name in a list avoids prepending the original dir. - program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] - if sys.platform == 'darwin': - configuration = self.configuration_dirname() - os.environ['DYLD_LIBRARY_PATH'] = os.path.join('out', configuration) - return self.run(program=program, *args, **kw) - - def built_file_path(self, name, type=None, **kw): - result = [] - chdir = kw.get('chdir') - if chdir: - result.append(chdir) - result.append('out') - result.append(self.configuration_dirname()) - if type == self.STATIC_LIB: - if sys.platform != 'darwin': - result.append('obj') - elif type == self.SHARED_LIB: - if sys.platform != 'darwin' and sys.platform != 'win32': - result.append('lib') - subdir = kw.get('subdir') - if subdir and type != self.SHARED_LIB: - result.append(subdir) - result.append(self.built_file_basename(name, type, **kw)) - return self.workpath(*result) - - def up_to_date(self, gyp_file, target=None, **kw): - result = self.build(gyp_file, target, **kw) - if not result: - stdout = self.stdout() - if 'ninja: no work to do' not in stdout: - self.report_not_up_to_date() - self.fail_test() - return result - - -class TestGypMSVS(TestGypOnMSToolchain): - """ - Subclass for testing the GYP Visual Studio generator. - """ - format = 'msvs' - - u = r'=== Build: 0 succeeded, 0 failed, (\d+) up-to-date, 0 skipped ===' - up_to_date_re = re.compile(u, re.M) - - # Initial None element will indicate to our .initialize_build_tool() - # method below that 'devenv' was not found on %PATH%. - # - # Note: we must use devenv.com to be able to capture build output. - # Directly executing devenv.exe only sends output to BuildLog.htm. - build_tool_list = [None, 'devenv.com'] - - def initialize_build_tool(self): - super(TestGypMSVS, self).initialize_build_tool() - self.build_tool = self.devenv_path - - def build(self, gyp_file, target=None, rebuild=False, clean=False, **kw): - """ - Runs a Visual Studio build using the configuration generated - from the specified gyp_file. - """ - configuration = self.configuration_buildname() - if clean: - build = '/Clean' - elif rebuild: - build = '/Rebuild' - else: - build = '/Build' - arguments = kw.get('arguments', [])[:] - arguments.extend([gyp_file.replace('.gyp', '.sln'), - build, configuration]) - # Note: the Visual Studio generator doesn't add an explicit 'all' - # target, so we just treat it the same as the default. - if target not in (None, self.ALL, self.DEFAULT): - arguments.extend(['/Project', target]) - if self.configuration: - arguments.extend(['/ProjectConfig', self.configuration]) - kw['arguments'] = arguments - return self.run(program=self.build_tool, **kw) - def up_to_date(self, gyp_file, target=None, **kw): - """ - Verifies that a build of the specified Visual Studio target is up to date. - - Beware that VS2010 will behave strangely if you build under - C:\USERS\yourname\AppData\Local. It will cause needless work. The ouptut - will be "1 succeeded and 0 up to date". MSBuild tracing reveals that: - "Project 'C:\Users\...\AppData\Local\...vcxproj' not up to date because - 'C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO 10.0\VC\BIN\1033\CLUI.DLL' - was modified at 02/21/2011 17:03:30, which is newer than '' which was - modified at 01/01/0001 00:00:00. - - The workaround is to specify a workdir when instantiating the test, e.g. - test = TestGyp.TestGyp(workdir='workarea') - """ - result = self.build(gyp_file, target, **kw) - if not result: - stdout = self.stdout() - - m = self.up_to_date_re.search(stdout) - up_to_date = m and int(m.group(1)) > 0 - if not up_to_date: - self.report_not_up_to_date() - self.fail_test() - return result - def run_built_executable(self, name, *args, **kw): - """ - Runs an executable built by Visual Studio. - """ - configuration = self.configuration_dirname() - # Enclosing the name in a list avoids prepending the original dir. - program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] - return self.run(program=program, *args, **kw) - def built_file_path(self, name, type=None, **kw): - """ - Returns a path to the specified file name, of the specified type, - as built by Visual Studio. - - Built files are in a subdirectory that matches the configuration - name. The default is 'Default'. - - A chdir= keyword argument specifies the source directory - relative to which the output subdirectory can be found. - - "type" values of STATIC_LIB or SHARED_LIB append the necessary - prefixes and suffixes to a platform-independent library base name. - """ - result = [] - chdir = kw.get('chdir') - if chdir: - result.append(chdir) - result.append(self.configuration_dirname()) - if type == self.STATIC_LIB: - result.append('lib') - result.append(self.built_file_basename(name, type, **kw)) - return self.workpath(*result) - - -class TestGypMSVSNinja(TestGypNinja): - """ - Subclass for testing the GYP Visual Studio Ninja generator. - """ - format = 'msvs-ninja' - - def initialize_build_tool(self): - super(TestGypMSVSNinja, self).initialize_build_tool() - # When using '--build', make sure ninja is first in the format list. - self.formats.insert(0, 'ninja') - - def build(self, gyp_file, target=None, rebuild=False, clean=False, **kw): - """ - Runs a Visual Studio build using the configuration generated - from the specified gyp_file. - """ - arguments = kw.get('arguments', [])[:] - if target in (None, self.ALL, self.DEFAULT): - # Note: the Visual Studio generator doesn't add an explicit 'all' target. - # This will build each project. This will work if projects are hermetic, - # but may fail if they are not (a project may run more than once). - # It would be nice to supply an all.metaproj for MSBuild. - arguments.extend([gyp_file.replace('.gyp', '.sln')]) - else: - # MSBuild documentation claims that one can specify a sln but then build a - # project target like 'msbuild a.sln /t:proj:target' but this format only - # supports 'Clean', 'Rebuild', and 'Publish' (with none meaning Default). - # This limitation is due to the .sln -> .sln.metaproj conversion. - # The ':' is not special, 'proj:target' is a target in the metaproj. - arguments.extend([target+'.vcxproj']) - - if clean: - build = 'Clean' - elif rebuild: - build = 'Rebuild' - else: - build = 'Build' - arguments.extend(['/target:'+build]) - configuration = self.configuration_buildname() - config = configuration.split('|') - arguments.extend(['/property:Configuration='+config[0]]) - if len(config) > 1: - arguments.extend(['/property:Platform='+config[1]]) - arguments.extend(['/property:BuildInParallel=false']) - arguments.extend(['/verbosity:minimal']) - - kw['arguments'] = arguments - return self.run(program=self.msbuild_path, **kw) - - -class TestGypXcode(TestGypBase): - """ - Subclass for testing the GYP Xcode generator. - """ - format = 'xcode' - build_tool_list = ['xcodebuild'] - - phase_script_execution = ("\n" - "PhaseScriptExecution /\\S+/Script-[0-9A-F]+\\.sh\n" - " cd /\\S+\n" - " /bin/sh -c /\\S+/Script-[0-9A-F]+\\.sh\n" - "(make: Nothing to be done for `all'\\.\n)?") - - strip_up_to_date_expressions = [ - # Various actions or rules can run even when the overall build target - # is up to date. Strip those phases' GYP-generated output. - re.compile(phase_script_execution, re.S), - - # The message from distcc_pump can trail the "BUILD SUCCEEDED" - # message, so strip that, too. - re.compile('__________Shutting down distcc-pump include server\n', re.S), - ] - - up_to_date_endings = ( - 'Checking Dependencies...\n** BUILD SUCCEEDED **\n', # Xcode 3.0/3.1 - 'Check dependencies\n** BUILD SUCCEEDED **\n\n', # Xcode 3.2 - 'Check dependencies\n\n\n** BUILD SUCCEEDED **\n\n', # Xcode 4.2 - 'Check dependencies\n\n** BUILD SUCCEEDED **\n\n', # Xcode 5.0 - ) - - def build(self, gyp_file, target=None, **kw): - """ - Runs an xcodebuild using the .xcodeproj generated from the specified - gyp_file. - """ - # Be sure we're working with a copy of 'arguments' since we modify it. - # The caller may not be expecting it to be modified. - arguments = kw.get('arguments', [])[:] - arguments.extend(['-project', gyp_file.replace('.gyp', '.xcodeproj')]) - if target == self.ALL: - arguments.append('-alltargets',) - elif target not in (None, self.DEFAULT): - arguments.extend(['-target', target]) - if self.configuration: - arguments.extend(['-configuration', self.configuration]) - symroot = kw.get('SYMROOT', '$SRCROOT/build') - if symroot: - arguments.append('SYMROOT='+symroot) - kw['arguments'] = arguments - - # Work around spurious stderr output from Xcode 4, http://crbug.com/181012 - match = kw.pop('match', self.match) - def match_filter_xcode(actual, expected): - if actual: - if not TestCmd.is_List(actual): - actual = actual.split('\n') - if not TestCmd.is_List(expected): - expected = expected.split('\n') - actual = [a for a in actual - if 'No recorder, buildTask: <Xcode3BuildTask:' not in a] - return match(actual, expected) - kw['match'] = match_filter_xcode - - return self.run(program=self.build_tool, **kw) - def up_to_date(self, gyp_file, target=None, **kw): - """ - Verifies that a build of the specified Xcode target is up to date. - """ - result = self.build(gyp_file, target, **kw) - if not result: - output = self.stdout() - for expression in self.strip_up_to_date_expressions: - output = expression.sub('', output) - if not output.endswith(self.up_to_date_endings): - self.report_not_up_to_date() - self.fail_test() - return result - def run_built_executable(self, name, *args, **kw): - """ - Runs an executable built by xcodebuild. - """ - configuration = self.configuration_dirname() - os.environ['DYLD_LIBRARY_PATH'] = os.path.join('build', configuration) - # Enclosing the name in a list avoids prepending the original dir. - program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] - return self.run(program=program, *args, **kw) - def built_file_path(self, name, type=None, **kw): - """ - Returns a path to the specified file name, of the specified type, - as built by Xcode. - - Built files are in the subdirectory 'build/{configuration}'. - The default is 'build/Default'. - - A chdir= keyword argument specifies the source directory - relative to which the output subdirectory can be found. - - "type" values of STATIC_LIB or SHARED_LIB append the necessary - prefixes and suffixes to a platform-independent library base name. - """ - result = [] - chdir = kw.get('chdir') - if chdir: - result.append(chdir) - configuration = self.configuration_dirname() - result.extend(['build', configuration]) - result.append(self.built_file_basename(name, type, **kw)) - return self.workpath(*result) - - -class TestGypXcodeNinja(TestGypXcode): - """ - Subclass for testing the GYP Xcode Ninja generator. - """ - format = 'xcode-ninja' - - def initialize_build_tool(self): - super(TestGypXcodeNinja, self).initialize_build_tool() - # When using '--build', make sure ninja is first in the format list. - self.formats.insert(0, 'ninja') - - def build(self, gyp_file, target=None, **kw): - """ - Runs an xcodebuild using the .xcodeproj generated from the specified - gyp_file. - """ - build_config = self.configuration - if build_config and build_config.endswith(('-iphoneos', - '-iphonesimulator')): - build_config, sdk = self.configuration.split('-') - kw['arguments'] = kw.get('arguments', []) + ['-sdk', sdk] - - with self._build_configuration(build_config): - return super(TestGypXcodeNinja, self).build( - gyp_file.replace('.gyp', '.ninja.gyp'), target, **kw) - - @contextmanager - def _build_configuration(self, build_config): - config = self.configuration - self.configuration = build_config - try: - yield - finally: - self.configuration = config - - def built_file_path(self, name, type=None, **kw): - result = [] - chdir = kw.get('chdir') - if chdir: - result.append(chdir) - result.append('out') - result.append(self.configuration_dirname()) - subdir = kw.get('subdir') - if subdir and type != self.SHARED_LIB: - result.append(subdir) - result.append(self.built_file_basename(name, type, **kw)) - return self.workpath(*result) - - def up_to_date(self, gyp_file, target=None, **kw): - result = self.build(gyp_file, target, **kw) - if not result: - stdout = self.stdout() - if 'ninja: no work to do' not in stdout: - self.report_not_up_to_date() - self.fail_test() - return result - - def run_built_executable(self, name, *args, **kw): - """ - Runs an executable built by xcodebuild + ninja. - """ - configuration = self.configuration_dirname() - os.environ['DYLD_LIBRARY_PATH'] = os.path.join('out', configuration) - # Enclosing the name in a list avoids prepending the original dir. - program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] - return self.run(program=program, *args, **kw) - - -format_class_list = [ - TestGypGypd, - TestGypCMake, - TestGypMake, - TestGypMSVS, - TestGypMSVSNinja, - TestGypNinja, - TestGypXcode, - TestGypXcodeNinja, -] - -def TestGyp(*args, **kw): - """ - Returns an appropriate TestGyp* instance for a specified GYP format. - """ - format = kw.pop('format', os.environ.get('TESTGYP_FORMAT')) - for format_class in format_class_list: - if format == format_class.format: - return format_class(*args, **kw) - raise Exception, "unknown format %r" % format |