OLD | NEW |
---|---|
(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 | |
18 import os.path | |
19 import subprocess | |
20 import gyp | |
21 import gyp.common | |
22 import shlex | |
23 | |
24 generator_wants_static_library_dependencies_adjusted = False | |
25 | |
26 generator_default_variables = { | |
27 } | |
28 | |
29 for dirname in ['INTERMEDIATE_DIR', 'PRODUCT_DIR', 'LIB_DIR', 'SHARED_LIB_DIR']: | |
30 # Some gyp steps fail if these are empty(!). | |
31 generator_default_variables[dirname] = 'dir' | |
32 | |
33 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', | |
34 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT', | |
35 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX', | |
36 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX', | |
37 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX']: | |
38 generator_default_variables[unused] = '' | |
39 | |
40 # Include dirs will occasionaly use the SHARED_INTERMEDIATE_DIR variable as | |
41 # part of the path when dealing with generated headers. This value will be | |
42 # replaced dynamically for each configuration. | |
43 generator_default_variables['SHARED_INTERMEDIATE_DIR'] = \ | |
44 '$SHARED_INTERMEDIATES_DIR' | |
45 | |
46 | |
47 def CalculateVariables(default_variables, params): | |
48 generator_flags = params.get('generator_flags', {}) | |
49 for key, val in generator_flags.items(): | |
50 default_variables.setdefault(key, val) | |
51 default_variables.setdefault('OS', gyp.common.GetFlavor(params)) | |
52 | |
53 | |
54 def CalculateGeneratorInputInfo(params): | |
55 """Calculate the generator specific info that gets fed to input (called by | |
56 gyp).""" | |
57 generator_flags = params.get('generator_flags', {}) | |
58 if generator_flags.get('adjust_static_libraries', False): | |
59 global generator_wants_static_library_dependencies_adjusted | |
60 generator_wants_static_library_dependencies_adjusted = True | |
61 | |
62 | |
63 def GetAllIncludeDirs(target_list, target_dicts, shared_intermediates_dir, | |
Nico
2012/04/27 17:33:41
nit: style guide says no abbreviations. Use GetAll
Jesse Greenwald
2012/04/28 01:57:49
Done.
| |
64 config_name): | |
65 """Calculate the set of include dirs to be used. | |
66 | |
67 Returns: | |
68 A list including all the include_dir's specified for every target followed | |
69 by any include dirs that were added as cflag compiler options. | |
70 """ | |
71 | |
72 gyp_includes = [] | |
73 compiler_includes = [] | |
74 | |
75 for target_name in target_list: | |
76 target = target_dicts[target_name] | |
77 if config_name in target['configurations']: | |
78 config = target['configurations'][config_name] | |
79 | |
80 # Look for any include dirs that were explicitly added via cflags. This | |
81 # may be done in gyp files to force certain includes to come at the end. | |
Nico
2012/04/27 17:33:41
Can you at least put a "# TODO: Change the gyp fil
Jesse Greenwald
2012/04/28 01:57:49
Done.
| |
82 cflags = config['cflags'] | |
83 for cflag in cflags: | |
84 include_dir = '' | |
85 if cflag.startswith('-I'): | |
86 include_dir = cflag[2:] | |
87 if include_dir and not include_dir in compiler_includes: | |
88 compiler_includes.append(include_dir) | |
89 | |
90 # Find standard gyp include dirs. | |
91 if config.has_key('include_dirs'): | |
92 include_dirs = config['include_dirs'] | |
93 for include_dir in include_dirs: | |
94 include_dir = include_dir.replace('$SHARED_INTERMEDIATES_DIR', | |
95 shared_intermediates_dir) | |
96 if not os.path.isabs(include_dir): | |
97 base_dir = os.path.dirname(target_name) | |
98 | |
99 include_dir = base_dir + '/' + include_dir | |
100 include_dir = os.path.abspath(include_dir) | |
101 | |
102 if not include_dir in gyp_includes: | |
103 gyp_includes.append(include_dir) | |
104 | |
105 gyp_includes.sort() | |
106 | |
107 # Generate a list that has all the include dirs. | |
108 all_includes = list(gyp_includes) | |
Nico
2012/04/27 17:33:41
s/list/set/, else the loop below is O(n^2). (Also,
Jesse Greenwald
2012/04/28 01:57:49
To keep the ordering, I copied gyp_includes into a
| |
109 for compiler_include in compiler_includes: | |
110 if not compiler_include in all_includes: | |
111 all_includes.append(compiler_include) | |
112 | |
113 # All done. | |
114 return all_includes | |
115 | |
116 | |
117 def GetCompilerPath(target_list, target_dicts, data): | |
118 """Determine a command that can be used to invoke the compiler. | |
119 | |
120 Returns: | |
121 If this is a gyp project that has explicit make settings, try to determine | |
122 the compiler from that. Otherwise, see if a compiler was specified via the | |
123 CC_target environment variable. | |
124 """ | |
125 | |
126 # First, see if the compiler is configured in make's settings. | |
127 build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0]) | |
128 make_global_settings_dict = data[build_file].get('make_global_settings', {}) | |
129 for key, value in make_global_settings_dict: | |
130 if key in ['CC', 'CXX']: | |
131 return value | |
132 | |
133 # Check to see if the compiler was specified as an environment variable. | |
Nico
2012/04/27 17:33:41
I think ninja allows 'CC' and 'CXX' as env keys to
Jesse Greenwald
2012/04/28 01:57:49
Done.
| |
134 cc_target = os.environ.get('CC_target') | |
135 if cc_target: | |
136 return cc_target | |
137 | |
138 # Default. | |
Nico
2012/04/27 17:33:41
Nit: Comment doesn't add much, remove it.
Jesse Greenwald
2012/04/28 01:57:49
Done.
| |
139 return 'gcc' | |
140 | |
141 | |
142 def GetAllDefines(target_list, target_dicts, data, config_name): | |
143 """Calculate the defines for a project. | |
144 | |
145 Returns: | |
146 A dict that includes explict defines declared in gyp files along with all of | |
147 the default defines that the compiler uses. | |
148 """ | |
149 | |
150 # Get defines declared in the gyp files. | |
151 all_defines = {} | |
152 for target_name in target_list: | |
153 target = target_dicts[target_name] | |
154 | |
155 if config_name in target['configurations']: | |
156 config = target['configurations'][config_name] | |
157 for define in config['defines']: | |
158 split_define = define.split('=', 1) | |
159 if len(split_define) == 1: | |
160 split_define.append('1') | |
161 if split_define[0].strip() in all_defines: | |
162 # Already defined | |
163 continue | |
164 | |
165 all_defines[split_define[0].strip()] = split_define[1].strip() | |
166 | |
167 # Get default compiler defines (if possible). | |
Nico
2012/04/27 17:33:41
I'm surprised this part is needed. Shouldn't Eclip
Jesse Greenwald
2012/04/28 01:57:49
Eclipse doesn't know what compiler is being used.
| |
168 cc_target = GetCompilerPath(target_list, target_dicts, data) | |
169 if cc_target: | |
170 command = shlex.split(cc_target) | |
171 command.extend(['-E', '-dM', '-']) | |
172 cpp_proc = subprocess.Popen(args=command, cwd='.', | |
173 stdin=subprocess.PIPE, stdout=subprocess.PIPE) | |
174 cpp_output = cpp_proc.communicate()[0] | |
175 cpp_lines = cpp_output.split('\n') | |
176 for cpp_line in cpp_lines: | |
177 if not cpp_line.strip(): | |
178 continue | |
179 cpp_line_parts = cpp_line.split(' ', 2) | |
180 key = cpp_line_parts[1] | |
181 if len(cpp_line_parts) >= 3: | |
182 val = cpp_line_parts[2] | |
183 else: | |
184 val = '1' | |
185 all_defines[key] = val | |
186 | |
187 return all_defines | |
188 | |
189 | |
190 def WriteIncludePaths(out, eclipse_langs, include_dirs): | |
191 """Write the includes section of a CDT settings export file.""" | |
192 | |
193 out.write(' <section name="org.eclipse.cdt.internal.ui.wizards.' \ | |
194 'settingswizards.IncludePaths">\n') | |
195 out.write(' <language name="holder for library settings"></language>\n') | |
196 for lang in eclipse_langs: | |
197 out.write(' <language name="%s">\n' % lang) | |
198 for include_dir in include_dirs: | |
199 out.write(' <includepath workspace_path="false">%s</includepath>\n' % | |
200 include_dir) | |
201 out.write(' </language>\n') | |
202 out.write(' </section>\n') | |
203 | |
204 | |
205 def WriteMacros(out, eclipse_langs, defines): | |
206 """Write the macros section of a CDT settings export file.""" | |
207 | |
208 out.write(' <section name="org.eclipse.cdt.internal.ui.wizards.' \ | |
209 'settingswizards.Macros">\n') | |
210 out.write(' <language name="holder for library settings"></language>\n') | |
211 for lang in eclipse_langs: | |
212 out.write(' <language name="%s">\n' % lang) | |
213 for key in sorted(defines.iterkeys()): | |
214 out.write(' <macro><name>%s</name><value>%s</value></macro>\n' % | |
215 (key, defines[key])) | |
216 out.write(' </language>\n') | |
217 out.write(' </section>\n') | |
218 | |
219 | |
220 def GenerateOutputForConfig(target_list, target_dicts, data, params, | |
221 config_name): | |
222 options = params['options'] | |
223 generator_flags = params.get('generator_flags', {}) | |
224 | |
225 # build_dir: relative path from source root to our output files. | |
226 # e.g. "out/Debug" | |
227 build_dir = os.path.join(generator_flags.get('output_dir', 'out'), | |
228 config_name) | |
229 | |
230 toplevel_build = os.path.join(options.toplevel_dir, build_dir) | |
231 shared_intermediate_dir = os.path.join(toplevel_build, 'obj', 'gen') | |
232 | |
233 if not os.path.exists(toplevel_build): | |
234 os.makedirs(toplevel_build) | |
235 out = open(os.path.join(toplevel_build, 'eclipse-cdt-settings.xml'), 'w') | |
236 | |
237 out.write('<?xml version="1.0" encoding="UTF-8"?>\n') | |
238 out.write('<cdtprojectproperties>\n') | |
239 | |
240 eclipse_langs = ['C++ Source File', 'C Source File', 'Assembly Source File', | |
241 'GNU C++', 'GNU C', 'Assembly'] | |
242 include_dirs = GetAllIncludeDirs(target_list, target_dicts, | |
243 shared_intermediate_dir, config_name) | |
244 WriteIncludePaths(out, eclipse_langs, include_dirs) | |
245 defines = GetAllDefines(target_list, target_dicts, data, config_name) | |
246 WriteMacros(out, eclipse_langs, defines) | |
247 | |
248 out.write('</cdtprojectproperties>\n') | |
249 out.close() | |
250 | |
251 | |
252 def GenerateOutput(target_list, target_dicts, data, params): | |
253 """Generate an XML settings file that can be imported into a CDT project.""" | |
254 | |
255 if params['options'].generator_output: | |
256 raise NotImplementedError, "--generator_output not implemented for eclipse" | |
257 | |
258 user_config = params.get('generator_flags', {}).get('config', None) | |
259 if user_config: | |
260 GenerateOutputForConfig(target_list, target_dicts, data, params, | |
261 user_config) | |
262 else: | |
263 config_names = target_dicts[target_list[0]]['configurations'].keys() | |
264 for config_name in config_names: | |
265 GenerateOutputForConfig(target_list, target_dicts, data, params, | |
266 config_name) | |
267 | |
OLD | NEW |