| Index: pylib/gyp/generator/eclipse.py
|
| diff --git a/pylib/gyp/generator/eclipse.py b/pylib/gyp/generator/eclipse.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5e16c02739df5974ae846397aebea91e6b9b3f63
|
| --- /dev/null
|
| +++ b/pylib/gyp/generator/eclipse.py
|
| @@ -0,0 +1,271 @@
|
| +# 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.
|
| +
|
| +"""GYP backend that generates Eclipse CDT settings files.
|
| +
|
| +This backend DOES NOT generate Eclipse CDT projects. Instead, it generates XML
|
| +files that can be imported into an Eclipse CDT project. The XML file contains a
|
| +list of include paths and symbols (i.e. defines).
|
| +
|
| +Because a full .cproject definition is not created by this generator, it's not
|
| +possible to properly define the include dirs and symbols for each file
|
| +individually. Instead, one set of includes/symbols is generated for the entire
|
| +project. This works fairly well (and is a vast improvement in general), but may
|
| +still result in a few indexer issues here and there.
|
| +
|
| +This generator has no automated tests, so expect it to be broken.
|
| +"""
|
| +
|
| +import os.path
|
| +import subprocess
|
| +import gyp
|
| +import gyp.common
|
| +import shlex
|
| +
|
| +generator_wants_static_library_dependencies_adjusted = False
|
| +
|
| +generator_default_variables = {
|
| +}
|
| +
|
| +for dirname in ['INTERMEDIATE_DIR', 'PRODUCT_DIR', 'LIB_DIR', 'SHARED_LIB_DIR']:
|
| + # Some gyp steps fail if these are empty(!).
|
| + generator_default_variables[dirname] = 'dir'
|
| +
|
| +for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
|
| + 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT',
|
| + 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX',
|
| + 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX',
|
| + 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX']:
|
| + generator_default_variables[unused] = ''
|
| +
|
| +# Include dirs will occasionaly use the SHARED_INTERMEDIATE_DIR variable as
|
| +# part of the path when dealing with generated headers. This value will be
|
| +# replaced dynamically for each configuration.
|
| +generator_default_variables['SHARED_INTERMEDIATE_DIR'] = \
|
| + '$SHARED_INTERMEDIATES_DIR'
|
| +
|
| +
|
| +def CalculateVariables(default_variables, params):
|
| + generator_flags = params.get('generator_flags', {})
|
| + for key, val in generator_flags.items():
|
| + default_variables.setdefault(key, val)
|
| + default_variables.setdefault('OS', gyp.common.GetFlavor(params))
|
| +
|
| +
|
| +def CalculateGeneratorInputInfo(params):
|
| + """Calculate the generator specific info that gets fed to input (called by
|
| + gyp)."""
|
| + generator_flags = params.get('generator_flags', {})
|
| + if generator_flags.get('adjust_static_libraries', False):
|
| + global generator_wants_static_library_dependencies_adjusted
|
| + generator_wants_static_library_dependencies_adjusted = True
|
| +
|
| +
|
| +def GetAllIncludeDirectories(target_list, target_dicts,
|
| + shared_intermediates_dir, config_name):
|
| + """Calculate the set of include directories to be used.
|
| +
|
| + Returns:
|
| + A list including all the include_dir's specified for every target followed
|
| + by any include directories that were added as cflag compiler options.
|
| + """
|
| +
|
| + gyp_includes_set = set()
|
| + compiler_includes_list = []
|
| +
|
| + for target_name in target_list:
|
| + target = target_dicts[target_name]
|
| + if config_name in target['configurations']:
|
| + config = target['configurations'][config_name]
|
| +
|
| + # Look for any include dirs that were explicitly added via cflags. This
|
| + # may be done in gyp files to force certain includes to come at the end.
|
| + # TODO(jgreenwald): Change the gyp files to not abuse cflags for this, and
|
| + # remove this.
|
| + cflags = config['cflags']
|
| + for cflag in cflags:
|
| + include_dir = ''
|
| + if cflag.startswith('-I'):
|
| + include_dir = cflag[2:]
|
| + if include_dir and not include_dir in compiler_includes_list:
|
| + compiler_includes_list.append(include_dir)
|
| +
|
| + # Find standard gyp include dirs.
|
| + if config.has_key('include_dirs'):
|
| + include_dirs = config['include_dirs']
|
| + for include_dir in include_dirs:
|
| + include_dir = include_dir.replace('$SHARED_INTERMEDIATES_DIR',
|
| + shared_intermediates_dir)
|
| + if not os.path.isabs(include_dir):
|
| + base_dir = os.path.dirname(target_name)
|
| +
|
| + include_dir = base_dir + '/' + include_dir
|
| + include_dir = os.path.abspath(include_dir)
|
| +
|
| + if not include_dir in gyp_includes_set:
|
| + gyp_includes_set.add(include_dir)
|
| +
|
| +
|
| + # Generate a list that has all the include dirs.
|
| + all_includes_list = list(gyp_includes_set)
|
| + all_includes_list.sort()
|
| + for compiler_include in compiler_includes_list:
|
| + if not compiler_include in gyp_includes_set:
|
| + all_includes_list.append(compiler_include)
|
| +
|
| + # All done.
|
| + return all_includes_list
|
| +
|
| +
|
| +def GetCompilerPath(target_list, target_dicts, data):
|
| + """Determine a command that can be used to invoke the compiler.
|
| +
|
| + Returns:
|
| + If this is a gyp project that has explicit make settings, try to determine
|
| + the compiler from that. Otherwise, see if a compiler was specified via the
|
| + CC_target environment variable.
|
| + """
|
| +
|
| + # First, see if the compiler is configured in make's settings.
|
| + build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
|
| + make_global_settings_dict = data[build_file].get('make_global_settings', {})
|
| + for key, value in make_global_settings_dict:
|
| + if key in ['CC', 'CXX']:
|
| + return value
|
| +
|
| + # Check to see if the compiler was specified as an environment variable.
|
| + for key in ['CC_target', 'CC', 'CXX']:
|
| + compiler = os.environ.get(key)
|
| + if compiler:
|
| + return compiler
|
| +
|
| + return 'gcc'
|
| +
|
| +
|
| +def GetAllDefines(target_list, target_dicts, data, config_name):
|
| + """Calculate the defines for a project.
|
| +
|
| + Returns:
|
| + A dict that includes explict defines declared in gyp files along with all of
|
| + the default defines that the compiler uses.
|
| + """
|
| +
|
| + # Get defines declared in the gyp files.
|
| + all_defines = {}
|
| + for target_name in target_list:
|
| + target = target_dicts[target_name]
|
| +
|
| + if config_name in target['configurations']:
|
| + config = target['configurations'][config_name]
|
| + for define in config['defines']:
|
| + split_define = define.split('=', 1)
|
| + if len(split_define) == 1:
|
| + split_define.append('1')
|
| + if split_define[0].strip() in all_defines:
|
| + # Already defined
|
| + continue
|
| +
|
| + all_defines[split_define[0].strip()] = split_define[1].strip()
|
| +
|
| + # Get default compiler defines (if possible).
|
| + cc_target = GetCompilerPath(target_list, target_dicts, data)
|
| + if cc_target:
|
| + command = shlex.split(cc_target)
|
| + command.extend(['-E', '-dM', '-'])
|
| + cpp_proc = subprocess.Popen(args=command, cwd='.',
|
| + stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
| + cpp_output = cpp_proc.communicate()[0]
|
| + cpp_lines = cpp_output.split('\n')
|
| + for cpp_line in cpp_lines:
|
| + if not cpp_line.strip():
|
| + continue
|
| + cpp_line_parts = cpp_line.split(' ', 2)
|
| + key = cpp_line_parts[1]
|
| + if len(cpp_line_parts) >= 3:
|
| + val = cpp_line_parts[2]
|
| + else:
|
| + val = '1'
|
| + all_defines[key] = val
|
| +
|
| + return all_defines
|
| +
|
| +
|
| +def WriteIncludePaths(out, eclipse_langs, include_dirs):
|
| + """Write the includes section of a CDT settings export file."""
|
| +
|
| + out.write(' <section name="org.eclipse.cdt.internal.ui.wizards.' \
|
| + 'settingswizards.IncludePaths">\n')
|
| + out.write(' <language name="holder for library settings"></language>\n')
|
| + for lang in eclipse_langs:
|
| + out.write(' <language name="%s">\n' % lang)
|
| + for include_dir in include_dirs:
|
| + out.write(' <includepath workspace_path="false">%s</includepath>\n' %
|
| + include_dir)
|
| + out.write(' </language>\n')
|
| + out.write(' </section>\n')
|
| +
|
| +
|
| +def WriteMacros(out, eclipse_langs, defines):
|
| + """Write the macros section of a CDT settings export file."""
|
| +
|
| + out.write(' <section name="org.eclipse.cdt.internal.ui.wizards.' \
|
| + 'settingswizards.Macros">\n')
|
| + out.write(' <language name="holder for library settings"></language>\n')
|
| + for lang in eclipse_langs:
|
| + out.write(' <language name="%s">\n' % lang)
|
| + for key in sorted(defines.iterkeys()):
|
| + out.write(' <macro><name>%s</name><value>%s</value></macro>\n' %
|
| + (key, defines[key]))
|
| + out.write(' </language>\n')
|
| + out.write(' </section>\n')
|
| +
|
| +
|
| +def GenerateOutputForConfig(target_list, target_dicts, data, params,
|
| + config_name):
|
| + options = params['options']
|
| + generator_flags = params.get('generator_flags', {})
|
| +
|
| + # build_dir: relative path from source root to our output files.
|
| + # e.g. "out/Debug"
|
| + build_dir = os.path.join(generator_flags.get('output_dir', 'out'),
|
| + config_name)
|
| +
|
| + toplevel_build = os.path.join(options.toplevel_dir, build_dir)
|
| + shared_intermediate_dir = os.path.join(toplevel_build, 'obj', 'gen')
|
| +
|
| + if not os.path.exists(toplevel_build):
|
| + os.makedirs(toplevel_build)
|
| + out = open(os.path.join(toplevel_build, 'eclipse-cdt-settings.xml'), 'w')
|
| +
|
| + out.write('<?xml version="1.0" encoding="UTF-8"?>\n')
|
| + out.write('<cdtprojectproperties>\n')
|
| +
|
| + eclipse_langs = ['C++ Source File', 'C Source File', 'Assembly Source File',
|
| + 'GNU C++', 'GNU C', 'Assembly']
|
| + include_dirs = GetAllIncludeDirectories(target_list, target_dicts,
|
| + shared_intermediate_dir, config_name)
|
| + WriteIncludePaths(out, eclipse_langs, include_dirs)
|
| + defines = GetAllDefines(target_list, target_dicts, data, config_name)
|
| + WriteMacros(out, eclipse_langs, defines)
|
| +
|
| + out.write('</cdtprojectproperties>\n')
|
| + out.close()
|
| +
|
| +
|
| +def GenerateOutput(target_list, target_dicts, data, params):
|
| + """Generate an XML settings file that can be imported into a CDT project."""
|
| +
|
| + if params['options'].generator_output:
|
| + raise NotImplementedError, "--generator_output not implemented for eclipse"
|
| +
|
| + user_config = params.get('generator_flags', {}).get('config', None)
|
| + if user_config:
|
| + GenerateOutputForConfig(target_list, target_dicts, data, params,
|
| + user_config)
|
| + else:
|
| + config_names = target_dicts[target_list[0]]['configurations'].keys()
|
| + for config_name in config_names:
|
| + GenerateOutputForConfig(target_list, target_dicts, data, params,
|
| + config_name)
|
| +
|
|
|