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