| #!/usr/bin/env python |
| # Copyright 2014 Google Inc. All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """Wrapper to generate build files from .gyp files.""" |
| |
| import argparse |
| import logging |
| import os |
| import shlex |
| import sys |
| import textwrap |
| |
| import bootstrap_path |
| import cobalt |
| from cobalt.build import config |
| from cobalt.build import gyp_utils |
| from cobalt.tools import paths |
| |
| |
| SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) |
| |
| # Represents all valid build configurations. |
| VALID_BUILD_CONFIGS = config.VALID_BUILD_CONFIGS |
| |
| # Represents all supported platforms. |
| VALID_PLATFORMS = config.VALID_PLATFORMS |
| |
| # Return values used by main(). |
| RETVAL_SUCCESS = 0 |
| RETVAL_ERROR = 1 |
| |
| def _ListAsString(items): |
| return ', '.join('"%s"' % x for x in items) |
| |
| |
| def _GetHostOS(): |
| """Returns the name of the hosting OS.""" |
| host_os_names = { |
| 'linux2': 'linux', |
| 'linux3': 'linux', |
| 'darwin': 'linux', |
| 'win32': 'win', |
| } |
| |
| if not sys.platform in host_os_names: |
| if sys.platform == 'cygwin': |
| logging.error('There may be several incompatibilities when trying to\n' |
| 'build from within cygwin. When using a bash shell is\n' |
| 'preferred, consider using msysgit instead (see\n' |
| 'https://msysgit.github.io).\n') |
| raise RuntimeError('Unsupported platform: %s' % sys.platform) |
| |
| return host_os_names[sys.platform] |
| |
| |
| def _ParseCommandLineArguments(argv): |
| """Parses command line arguments.""" |
| |
| parser = argparse.ArgumentParser( |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| description=textwrap.dedent(__doc__)) |
| |
| parser.add_argument('-C', '--config', dest='build_configs', metavar='CONFIG', |
| action='append', default=[], |
| choices=VALID_BUILD_CONFIGS, |
| help='Specifies build configurations. Supported ' |
| 'configurations are %s. Can be specified multiple ' |
| 'times, creates all configurations if nothing is ' |
| 'given.' % _ListAsString(VALID_BUILD_CONFIGS)) |
| |
| gyp_debug_options = gyp_utils.GypDebugOptions() |
| parser.add_argument('-D', '--debug', dest='debug', metavar='DEBUGMODE', |
| action='append', default=[], |
| choices=gyp_debug_options + ['all'], |
| help='Turn on a debugging mode for debugging GYP. ' |
| 'Supported modes are %s or "all" for all of ' |
| 'them.' % _ListAsString(gyp_debug_options)) |
| |
| parser.add_argument('--check', action='store_true', |
| help='Check format of gyp files.') |
| parser.add_argument('-v', '--verbose', dest='verbose_count', |
| default=0, action='count', |
| help='Verbose level (multiple times for more).') |
| parser.add_argument('platform', choices=VALID_PLATFORMS, |
| metavar='platform', |
| help='Target platform. Supported platforms are: %s.' % ( |
| _ListAsString(VALID_PLATFORMS))) |
| parser.add_argument('build_file', nargs='?', |
| default=os.path.join(SCRIPT_DIR, 'all.gyp'), |
| help='GYP build file. Uses all.gyp if nothing is given.') |
| |
| options = parser.parse_args(argv) |
| |
| if options.build_configs: |
| # Get rid of duplicates. |
| options.build_configs = list(set(options.build_configs)) |
| else: |
| options.build_configs = VALID_BUILD_CONFIGS |
| |
| return options |
| |
| |
| def _AppendIncludes(includes, args): |
| for include in includes: |
| args.append('-I{}'.format(include)) |
| |
| |
| def _AppendVariables(variables, args): |
| for var in variables: |
| args.append('-D{}={}'.format(var, variables[var])) |
| |
| |
| def _AppendGeneratorVariables(variables, args): |
| for var in variables: |
| args.append('-G{}={}'.format(var, variables[var])) |
| |
| |
| def _SetupLogging(options): |
| logging_level = logging.WARNING |
| if options.verbose_count == 1: |
| logging_level = logging.INFO |
| elif options.verbose_count >= 2: |
| logging_level = logging.DEBUG |
| |
| logging_format = '%(message)s' |
| logging.basicConfig(level=logging_level, |
| format=logging_format) |
| |
| |
| class _GypRunner(object): |
| """Responsible for running GYP.""" |
| |
| def __init__(self, options): |
| self.options = options |
| self.common_args = [] |
| self.platform_config = config.LoadPlatformConfig(options.platform) |
| if not self.platform_config: |
| raise RuntimeError('Unable to load platform configuration.') |
| |
| os.environ.update(self.platform_config.GetEnvironmentVariables()) |
| self._MakeCommonArguments() |
| |
| def BuildConfig(self, config_name): |
| """Builds the GYP file for a given config.""" |
| |
| # Make a copy of the common arguments. |
| args = self.common_args[:] |
| |
| # Add config specific variables. |
| variables = { |
| 'cobalt_config': config_name, |
| } |
| variables.update(self.platform_config.GetVariables(config_name)) |
| _AppendVariables(variables, args) |
| |
| # Add the symbolizer path for ASAN to allow translation of callstacks. |
| asan_symbolizer_path = '' |
| if variables.get('use_asan', 0) == 1: |
| compiler_commandline = shlex.split(os.environ.get('CC', '')) |
| for path in compiler_commandline: |
| if os.path.isfile(path): |
| test_path = os.path.join(os.path.dirname(path), 'llvm-symbolizer') |
| if os.path.isfile(test_path): |
| asan_symbolizer_path = test_path |
| break |
| asan_variables = { |
| 'asan_symbolizer_path': asan_symbolizer_path, |
| } |
| _AppendVariables(asan_variables, args) |
| |
| # Add config specific generator variables. |
| generator_config = '{}_{}'.format(self.options.platform, config_name) |
| |
| generator_variables = { |
| 'config': generator_config, |
| } |
| generator_variables.update( |
| self.platform_config.GetGeneratorVariables(config_name)) |
| _AppendGeneratorVariables(generator_variables, args) |
| |
| # That target build file goes last. |
| args.append(self.options.build_file) |
| |
| gyp = gyp_utils.GetGyp() |
| return gyp.main(args) |
| |
| def _MakeCommonArguments(self): |
| """Makes a list of GYP arguments that is common to all configurations.""" |
| |
| # Flavor will be used by the generator to figure out the target platform. |
| flavor = self.platform_config.GetBuildFlavor() |
| source_tree_dir = paths.REPOSITORY_ROOT |
| self.common_args = [ |
| # Set the build format |
| '--format={}-{}'.format(self.platform_config.GetBuildFormat(), flavor), |
| # Set the depth argument to the top level directory. This will define |
| # the DEPTH variable which is the relative path between the directory |
| # containing the processed build file and the top level directory. |
| '--depth={}'.format(source_tree_dir), |
| # Set the top of the source tree. |
| '--toplevel-dir={}'.format(source_tree_dir), |
| ] |
| |
| # Add the source tree root to the Python path so that build files can |
| # easily reference other build files in the source tree. |
| sys.path.append(source_tree_dir) |
| |
| # Pass through the debug options. |
| for debug in self.options.debug: |
| self.common_args.append('--debug=%s' % debug) |
| |
| if self.options.check: |
| self.common_args.append('--check') |
| |
| # Append GYP variables |
| variables = { |
| 'OS': 'lb_shell', |
| 'cobalt_fastbuild': os.environ.get('LB_FASTBUILD', 0), |
| 'cobalt_version': gyp_utils.GetBuildNumber(), |
| 'host_os': _GetHostOS(), |
| 'CC_HOST': os.environ.get('CC_HOST', os.environ.get('CC','')), |
| } |
| if self.platform_config.IsStarboard(): |
| variables['OS'] = 'starboard' |
| platform = self.platform_config.platform |
| platforms = gyp_utils.GetThirdPartyPlatforms() |
| if platform in platforms: |
| full_starboard_path = platforms[platform] |
| assert full_starboard_path[:len(source_tree_dir)] == source_tree_dir |
| starboard_path = full_starboard_path[len(source_tree_dir) + 1:] |
| starboard_path = starboard_path.replace(os.sep, '/') |
| assert starboard_path[0] not in [ os.sep, os.altsep ] |
| variables['starboard_path'] = starboard_path |
| _AppendVariables(variables, self.common_args) |
| |
| # Append generator variables. |
| generator_variables = { |
| # set output folder name, affects all generators but MSVS |
| 'output_dir': 'out', |
| } |
| _AppendGeneratorVariables(generator_variables, self.common_args) |
| |
| # Append GYPI includes which will be includes by GYP before any processed |
| # build file. |
| gyp_includes = [ |
| os.path.join(SCRIPT_DIR, 'config', 'base.gypi'), |
| # TODO: Consider removing dependency on common.gypi |
| # going forward by moving the required bits into config/base.gypi. |
| os.path.join(source_tree_dir, 'build', 'common.gypi'), |
| ] |
| gyp_includes.extend(self.platform_config.GetIncludes()) |
| _AppendIncludes(gyp_includes, self.common_args) |
| |
| |
| def main(argv): |
| if os.environ.get('GYP_DEFINES'): |
| logging.error('GYP_DEFINES environment variable is not supported.') |
| return RETVAL_ERROR |
| |
| options = _ParseCommandLineArguments(argv) |
| _SetupLogging(options) |
| |
| try: |
| gyp_runner = _GypRunner(options) |
| except RuntimeError as e: |
| logging.error(e) |
| return RETVAL_ERROR |
| |
| for config_name in options.build_configs: |
| logging.info('Building config: %s.', config_name) |
| gyp_return = gyp_runner.BuildConfig(config_name) |
| if gyp_return: |
| logging.error('Gyp failed with error code %d.', gyp_return) |
| return gyp_return |
| |
| logging.info('Done.') |
| return RETVAL_SUCCESS |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |