Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Side by Side Diff: pylib/gyp/generator/eclipse.py

Issue 9972015: Create a gyp generator for Eclipse CDT settings (Closed) Base URL: http://git.chromium.org/external/gyp.git@master
Patch Set: Fixed an abbreviation in a comment; optimized GetAllIncludeDirectories a little more Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2012 Google Inc. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """GYP backend that generates Eclipse CDT settings files.
6
7 This backend DOES NOT generate Eclipse CDT projects. Instead, it generates XML
8 files that can be imported into an Eclipse CDT project. The XML file contains a
9 list of include paths and symbols (i.e. defines).
10
11 Because a full .cproject definition is not created by this generator, it's not
12 possible to properly define the include dirs and symbols for each file
13 individually. Instead, one set of includes/symbols is generated for the entire
14 project. This works fairly well (and is a vast improvement in general), but may
15 still result in a few indexer issues here and there.
16
17 This generator has no automated tests, so expect it to be broken.
18 """
19
20 import os.path
21 import subprocess
22 import gyp
23 import gyp.common
24 import shlex
25
26 generator_wants_static_library_dependencies_adjusted = False
27
28 generator_default_variables = {
29 }
30
31 for dirname in ['INTERMEDIATE_DIR', 'PRODUCT_DIR', 'LIB_DIR', 'SHARED_LIB_DIR']:
32 # Some gyp steps fail if these are empty(!).
33 generator_default_variables[dirname] = 'dir'
34
35 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
36 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT',
37 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX',
38 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX',
39 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX']:
40 generator_default_variables[unused] = ''
41
42 # Include dirs will occasionaly use the SHARED_INTERMEDIATE_DIR variable as
43 # part of the path when dealing with generated headers. This value will be
44 # replaced dynamically for each configuration.
45 generator_default_variables['SHARED_INTERMEDIATE_DIR'] = \
46 '$SHARED_INTERMEDIATES_DIR'
47
48
49 def CalculateVariables(default_variables, params):
50 generator_flags = params.get('generator_flags', {})
51 for key, val in generator_flags.items():
52 default_variables.setdefault(key, val)
53 default_variables.setdefault('OS', gyp.common.GetFlavor(params))
54
55
56 def CalculateGeneratorInputInfo(params):
57 """Calculate the generator specific info that gets fed to input (called by
58 gyp)."""
59 generator_flags = params.get('generator_flags', {})
60 if generator_flags.get('adjust_static_libraries', False):
61 global generator_wants_static_library_dependencies_adjusted
62 generator_wants_static_library_dependencies_adjusted = True
63
64
65 def GetAllIncludeDirectories(target_list, target_dicts,
66 shared_intermediates_dir, config_name):
67 """Calculate the set of include directories to be used.
68
69 Returns:
70 A list including all the include_dir's specified for every target followed
71 by any include directories that were added as cflag compiler options.
72 """
73
74 gyp_includes_set = set()
75 compiler_includes_list = []
76
77 for target_name in target_list:
78 target = target_dicts[target_name]
79 if config_name in target['configurations']:
80 config = target['configurations'][config_name]
81
82 # Look for any include dirs that were explicitly added via cflags. This
83 # may be done in gyp files to force certain includes to come at the end.
84 # TODO(jgreenwald): Change the gyp files to not abuse cflags for this, and
85 # remove this.
86 cflags = config['cflags']
87 for cflag in cflags:
88 include_dir = ''
89 if cflag.startswith('-I'):
90 include_dir = cflag[2:]
91 if include_dir and not include_dir in compiler_includes_list:
92 compiler_includes_list.append(include_dir)
93
94 # Find standard gyp include dirs.
95 if config.has_key('include_dirs'):
96 include_dirs = config['include_dirs']
97 for include_dir in include_dirs:
98 include_dir = include_dir.replace('$SHARED_INTERMEDIATES_DIR',
99 shared_intermediates_dir)
100 if not os.path.isabs(include_dir):
101 base_dir = os.path.dirname(target_name)
102
103 include_dir = base_dir + '/' + include_dir
104 include_dir = os.path.abspath(include_dir)
105
106 if not include_dir in gyp_includes_set:
107 gyp_includes_set.add(include_dir)
108
109
110 # Generate a list that has all the include dirs.
111 all_includes_list = list(gyp_includes_set)
112 all_includes_list.sort()
113 for compiler_include in compiler_includes_list:
114 if not compiler_include in gyp_includes_set:
115 all_includes_list.append(compiler_include)
116
117 # All done.
118 return all_includes_list
119
120
121 def GetCompilerPath(target_list, target_dicts, data):
122 """Determine a command that can be used to invoke the compiler.
123
124 Returns:
125 If this is a gyp project that has explicit make settings, try to determine
126 the compiler from that. Otherwise, see if a compiler was specified via the
127 CC_target environment variable.
128 """
129
130 # First, see if the compiler is configured in make's settings.
131 build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
132 make_global_settings_dict = data[build_file].get('make_global_settings', {})
133 for key, value in make_global_settings_dict:
134 if key in ['CC', 'CXX']:
135 return value
136
137 # Check to see if the compiler was specified as an environment variable.
138 for key in ['CC_target', 'CC', 'CXX']:
139 compiler = os.environ.get(key)
140 if compiler:
141 return compiler
142
143 return 'gcc'
144
145
146 def GetAllDefines(target_list, target_dicts, data, config_name):
147 """Calculate the defines for a project.
148
149 Returns:
150 A dict that includes explict defines declared in gyp files along with all of
151 the default defines that the compiler uses.
152 """
153
154 # Get defines declared in the gyp files.
155 all_defines = {}
156 for target_name in target_list:
157 target = target_dicts[target_name]
158
159 if config_name in target['configurations']:
160 config = target['configurations'][config_name]
161 for define in config['defines']:
162 split_define = define.split('=', 1)
163 if len(split_define) == 1:
164 split_define.append('1')
165 if split_define[0].strip() in all_defines:
166 # Already defined
167 continue
168
169 all_defines[split_define[0].strip()] = split_define[1].strip()
170
171 # Get default compiler defines (if possible).
172 cc_target = GetCompilerPath(target_list, target_dicts, data)
173 if cc_target:
174 command = shlex.split(cc_target)
175 command.extend(['-E', '-dM', '-'])
176 cpp_proc = subprocess.Popen(args=command, cwd='.',
177 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
178 cpp_output = cpp_proc.communicate()[0]
179 cpp_lines = cpp_output.split('\n')
180 for cpp_line in cpp_lines:
181 if not cpp_line.strip():
182 continue
183 cpp_line_parts = cpp_line.split(' ', 2)
184 key = cpp_line_parts[1]
185 if len(cpp_line_parts) >= 3:
186 val = cpp_line_parts[2]
187 else:
188 val = '1'
189 all_defines[key] = val
190
191 return all_defines
192
193
194 def WriteIncludePaths(out, eclipse_langs, include_dirs):
195 """Write the includes section of a CDT settings export file."""
196
197 out.write(' <section name="org.eclipse.cdt.internal.ui.wizards.' \
198 'settingswizards.IncludePaths">\n')
199 out.write(' <language name="holder for library settings"></language>\n')
200 for lang in eclipse_langs:
201 out.write(' <language name="%s">\n' % lang)
202 for include_dir in include_dirs:
203 out.write(' <includepath workspace_path="false">%s</includepath>\n' %
204 include_dir)
205 out.write(' </language>\n')
206 out.write(' </section>\n')
207
208
209 def WriteMacros(out, eclipse_langs, defines):
210 """Write the macros section of a CDT settings export file."""
211
212 out.write(' <section name="org.eclipse.cdt.internal.ui.wizards.' \
213 'settingswizards.Macros">\n')
214 out.write(' <language name="holder for library settings"></language>\n')
215 for lang in eclipse_langs:
216 out.write(' <language name="%s">\n' % lang)
217 for key in sorted(defines.iterkeys()):
218 out.write(' <macro><name>%s</name><value>%s</value></macro>\n' %
219 (key, defines[key]))
220 out.write(' </language>\n')
221 out.write(' </section>\n')
222
223
224 def GenerateOutputForConfig(target_list, target_dicts, data, params,
225 config_name):
226 options = params['options']
227 generator_flags = params.get('generator_flags', {})
228
229 # build_dir: relative path from source root to our output files.
230 # e.g. "out/Debug"
231 build_dir = os.path.join(generator_flags.get('output_dir', 'out'),
232 config_name)
233
234 toplevel_build = os.path.join(options.toplevel_dir, build_dir)
235 shared_intermediate_dir = os.path.join(toplevel_build, 'obj', 'gen')
236
237 if not os.path.exists(toplevel_build):
238 os.makedirs(toplevel_build)
239 out = open(os.path.join(toplevel_build, 'eclipse-cdt-settings.xml'), 'w')
240
241 out.write('<?xml version="1.0" encoding="UTF-8"?>\n')
242 out.write('<cdtprojectproperties>\n')
243
244 eclipse_langs = ['C++ Source File', 'C Source File', 'Assembly Source File',
245 'GNU C++', 'GNU C', 'Assembly']
246 include_dirs = GetAllIncludeDirectories(target_list, target_dicts,
247 shared_intermediate_dir, config_name)
248 WriteIncludePaths(out, eclipse_langs, include_dirs)
249 defines = GetAllDefines(target_list, target_dicts, data, config_name)
250 WriteMacros(out, eclipse_langs, defines)
251
252 out.write('</cdtprojectproperties>\n')
253 out.close()
254
255
256 def GenerateOutput(target_list, target_dicts, data, params):
257 """Generate an XML settings file that can be imported into a CDT project."""
258
259 if params['options'].generator_output:
260 raise NotImplementedError, "--generator_output not implemented for eclipse"
261
262 user_config = params.get('generator_flags', {}).get('config', None)
263 if user_config:
264 GenerateOutputForConfig(target_list, target_dicts, data, params,
265 user_config)
266 else:
267 config_names = target_dicts[target_list[0]]['configurations'].keys()
268 for config_name in config_names:
269 GenerateOutputForConfig(target_list, target_dicts, data, params,
270 config_name)
271
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698