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) |
+ |