OLD | NEW |
---|---|
1 # Copyright (c) 2012 Google Inc. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """GYP backend that generates Eclipse CDT settings files. | 5 """GYP backend that generates Eclipse CDT settings files. |
6 | 6 |
7 This backend DOES NOT generate Eclipse CDT projects. Instead, it generates XML | 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 | 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). | 9 list of include paths and symbols (i.e. defines). |
10 | 10 |
11 Because a full .cproject definition is not created by this generator, it's not | 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 | 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 | 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 | 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. | 15 still result in a few indexer issues here and there. |
16 | 16 |
17 This generator has no automated tests, so expect it to be broken. | 17 This generator has no automated tests, so expect it to be broken. |
18 """ | 18 """ |
19 | 19 |
20 from xml.sax.saxutils import escape | 20 from xml.sax.saxutils import escape |
21 import os.path | 21 import os.path |
22 import subprocess | 22 import subprocess |
23 import gyp | 23 import gyp |
24 import gyp.common | 24 import gyp.common |
25 import gyp.msvs_emulation | 25 import gyp.msvs_emulation |
26 import shlex | 26 import shlex |
27 import xml.etree.cElementTree as ET | |
27 | 28 |
28 generator_wants_static_library_dependencies_adjusted = False | 29 generator_wants_static_library_dependencies_adjusted = False |
29 | 30 |
30 generator_default_variables = { | 31 generator_default_variables = { |
31 } | 32 } |
32 | 33 |
33 for dirname in ['INTERMEDIATE_DIR', 'PRODUCT_DIR', 'LIB_DIR', 'SHARED_LIB_DIR']: | 34 for dirname in ['INTERMEDIATE_DIR', 'PRODUCT_DIR', 'LIB_DIR', 'SHARED_LIB_DIR']: |
34 # Some gyp steps fail if these are empty(!). | 35 # Some gyp steps fail if these are empty(!), so we convert them to variables |
35 generator_default_variables[dirname] = 'dir' | 36 generator_default_variables[dirname] = '$' + dirname |
36 | 37 |
37 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', | 38 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', |
38 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT', | 39 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT', |
39 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX', | 40 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX', |
40 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX', | 41 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX', |
41 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX', | 42 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX', |
42 'CONFIGURATION_NAME']: | 43 'CONFIGURATION_NAME']: |
43 generator_default_variables[unused] = '' | 44 generator_default_variables[unused] = '' |
44 | 45 |
45 # Include dirs will occasionally use the SHARED_INTERMEDIATE_DIR variable as | 46 # Include dirs will occasionally use the SHARED_INTERMEDIATE_DIR variable as |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
287 # e.g. "out/Debug" | 288 # e.g. "out/Debug" |
288 build_dir = os.path.join(generator_flags.get('output_dir', 'out'), | 289 build_dir = os.path.join(generator_flags.get('output_dir', 'out'), |
289 config_name) | 290 config_name) |
290 | 291 |
291 toplevel_build = os.path.join(options.toplevel_dir, build_dir) | 292 toplevel_build = os.path.join(options.toplevel_dir, build_dir) |
292 # Ninja uses out/Debug/gen while make uses out/Debug/obj/gen as the | 293 # Ninja uses out/Debug/gen while make uses out/Debug/obj/gen as the |
293 # SHARED_INTERMEDIATE_DIR. Include both possible locations. | 294 # SHARED_INTERMEDIATE_DIR. Include both possible locations. |
294 shared_intermediate_dirs = [os.path.join(toplevel_build, 'obj', 'gen'), | 295 shared_intermediate_dirs = [os.path.join(toplevel_build, 'obj', 'gen'), |
295 os.path.join(toplevel_build, 'gen')] | 296 os.path.join(toplevel_build, 'gen')] |
296 | 297 |
297 out_name = os.path.join(toplevel_build, 'eclipse-cdt-settings.xml') | 298 GenerateCdtSettingsFile(target_list, |
299 target_dicts, | |
300 data, | |
301 params, | |
302 config_name, | |
303 os.path.join(toplevel_build, | |
304 'eclipse-cdt-settings.xml'), | |
305 options, | |
306 shared_intermediate_dirs) | |
307 GenerateClasspathFile(target_list, | |
308 target_dicts, | |
309 options.toplevel_dir, | |
310 toplevel_build, | |
311 os.path.join(toplevel_build, | |
312 'eclipse-classpath.xml')) | |
313 | |
314 | |
315 def GenerateCdtSettingsFile(target_list, target_dicts, data, params, | |
316 config_name, out_name, options, | |
317 shared_intermediate_dirs): | |
298 gyp.common.EnsureDirExists(out_name) | 318 gyp.common.EnsureDirExists(out_name) |
299 out = open(out_name, 'w') | 319 with open(out_name, 'w') as out: |
320 out.write('<?xml version="1.0" encoding="UTF-8"?>\n') | |
321 out.write('<cdtprojectproperties>\n') | |
300 | 322 |
301 out.write('<?xml version="1.0" encoding="UTF-8"?>\n') | 323 eclipse_langs = ['C++ Source File', 'C Source File', 'Assembly Source File', |
302 out.write('<cdtprojectproperties>\n') | 324 'GNU C++', 'GNU C', 'Assembly'] |
325 compiler_path = GetCompilerPath(target_list, data, options) | |
326 include_dirs = GetAllIncludeDirectories(target_list, target_dicts, | |
327 shared_intermediate_dirs, | |
328 config_name, params, compiler_path) | |
329 WriteIncludePaths(out, eclipse_langs, include_dirs) | |
330 defines = GetAllDefines(target_list, target_dicts, data, config_name, | |
331 params, compiler_path) | |
332 WriteMacros(out, eclipse_langs, defines) | |
303 | 333 |
304 eclipse_langs = ['C++ Source File', 'C Source File', 'Assembly Source File', | 334 out.write('</cdtprojectproperties>\n') |
305 'GNU C++', 'GNU C', 'Assembly'] | |
306 compiler_path = GetCompilerPath(target_list, data, options) | |
307 include_dirs = GetAllIncludeDirectories(target_list, target_dicts, | |
308 shared_intermediate_dirs, config_name, | |
309 params, compiler_path) | |
310 WriteIncludePaths(out, eclipse_langs, include_dirs) | |
311 defines = GetAllDefines(target_list, target_dicts, data, config_name, params, | |
312 compiler_path) | |
313 WriteMacros(out, eclipse_langs, defines) | |
314 | 335 |
315 out.write('</cdtprojectproperties>\n') | 336 |
316 out.close() | 337 def GenerateClasspathFile(target_list, target_dicts, toplevel_dir, |
338 toplevel_build, out_name): | |
339 '''Generates a classpath file suitable for symbol navigation and code | |
340 completion of Java code (such as in Android projects) by finding all | |
341 .java and .jar files used as action inputs.''' | |
342 gyp.common.EnsureDirExists(out_name) | |
343 result = ET.Element('classpath') | |
344 | |
345 def AddElements(kind, paths): | |
346 # First, we need to normalize the paths so they are all relative to the | |
347 # toplevel dir. | |
348 rel_paths = set() | |
349 for path in paths: | |
350 if os.path.isabs(path): | |
351 rel_paths.add(os.path.relpath(path, toplevel_dir)) | |
352 else: | |
353 rel_paths.add(path) | |
354 | |
355 for path in sorted(rel_paths): | |
356 entry_element = ET.SubElement(result, 'classpathentry') | |
357 entry_element.set('kind', kind) | |
358 entry_element.set('path', path) | |
359 | |
360 AddElements('lib', GetJavaJars(target_list, target_dicts, toplevel_dir)) | |
361 AddElements('src', GetJavaSourceDirs(target_list, target_dicts)) | |
362 # Include the standard JRE container and a dummy out folder | |
363 AddElements('con', ['org.eclipse.jdt.launching.JRE_CONTAINER']) | |
364 # Include a dummy out folder so that Eclipse doesn't use the default /bin | |
365 # folder in the root of the project. | |
366 AddElements('output', [os.path.join(toplevel_build, '.eclipse-java-build')]) | |
367 | |
368 ET.ElementTree(result).write(out_name) | |
369 | |
370 | |
371 def GetJavaJars(target_list, target_dicts, toplevel_dir): | |
372 '''Generates a sequence of all .jars used as inputs.''' | |
373 for target_name in target_list: | |
374 target = target_dicts[target_name] | |
375 for action in target.get('actions', []): | |
376 for input_ in action['inputs']: | |
377 if os.path.splitext(input_)[1] == '.jar' and not input_.startswith('$'): | |
378 if os.path.isabs(input_): | |
379 yield input_ | |
380 else: | |
381 yield os.path.join(os.path.dirname(target_name), input_) | |
382 | |
383 | |
384 def GetJavaSourceDirs(target_list, target_dicts): | |
385 '''Generates a sequence of all likely java package root directories.''' | |
386 for target_name in target_list: | |
387 target = target_dicts[target_name] | |
388 for action in target.get('actions', []): | |
389 for input_ in action['inputs']: | |
390 if os.path.splitext(input_)[1] == '.java' and \ | |
Nico
2014/12/08 17:59:00
nit: prefer
if (foo and
bar):
over
if
mckev
2014/12/09 21:42:41
Done.
| |
391 not input_.startswith('$'): | |
392 dir_ = os.path.dirname(os.path.join(os.path.dirname(target_name), | |
393 input_)) | |
394 # If there is a parent 'src' or 'java' folder, navigate up to it - | |
395 # these are canonical package root names. | |
Nico
2014/12/08 17:59:00
this seems a bit fragile (but i can't think of any
mckev
2014/12/09 21:42:41
I agree. I expanded the comment here to be more e
| |
396 parent_search = dir_ | |
397 while os.path.basename(parent_search) not in ['src', 'java']: | |
398 parent_search, _ = os.path.split(parent_search) | |
399 if not parent_search: | |
400 # Didn't find a known root, just return the original path | |
401 yield dir_ | |
402 break | |
403 else: | |
404 yield parent_search | |
317 | 405 |
318 | 406 |
319 def GenerateOutput(target_list, target_dicts, data, params): | 407 def GenerateOutput(target_list, target_dicts, data, params): |
320 """Generate an XML settings file that can be imported into a CDT project.""" | 408 """Generate an XML settings file that can be imported into a CDT project.""" |
321 | 409 |
322 if params['options'].generator_output: | 410 if params['options'].generator_output: |
323 raise NotImplementedError("--generator_output not implemented for eclipse") | 411 raise NotImplementedError("--generator_output not implemented for eclipse") |
324 | 412 |
325 user_config = params.get('generator_flags', {}).get('config', None) | 413 user_config = params.get('generator_flags', {}).get('config', None) |
326 if user_config: | 414 if user_config: |
327 GenerateOutputForConfig(target_list, target_dicts, data, params, | 415 GenerateOutputForConfig(target_list, target_dicts, data, params, |
328 user_config) | 416 user_config) |
329 else: | 417 else: |
330 config_names = target_dicts[target_list[0]]['configurations'].keys() | 418 config_names = target_dicts[target_list[0]]['configurations'].keys() |
331 for config_name in config_names: | 419 for config_name in config_names: |
332 GenerateOutputForConfig(target_list, target_dicts, data, params, | 420 GenerateOutputForConfig(target_list, target_dicts, data, params, |
333 config_name) | 421 config_name) |
334 | 422 |
OLD | NEW |