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 |
| 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 in Chromium. This will |
| 396 # break if 'src' or 'java' exists in the package structure. This |
| 397 # could be further improved by inspecting the java file for the |
| 398 # package name if this proves to be too fragile in practice. |
| 399 parent_search = dir_ |
| 400 while os.path.basename(parent_search) not in ['src', 'java']: |
| 401 parent_search, _ = os.path.split(parent_search) |
| 402 if not parent_search: |
| 403 # Didn't find a known root, just return the original path |
| 404 yield dir_ |
| 405 break |
| 406 else: |
| 407 yield parent_search |
317 | 408 |
318 | 409 |
319 def GenerateOutput(target_list, target_dicts, data, params): | 410 def GenerateOutput(target_list, target_dicts, data, params): |
320 """Generate an XML settings file that can be imported into a CDT project.""" | 411 """Generate an XML settings file that can be imported into a CDT project.""" |
321 | 412 |
322 if params['options'].generator_output: | 413 if params['options'].generator_output: |
323 raise NotImplementedError("--generator_output not implemented for eclipse") | 414 raise NotImplementedError("--generator_output not implemented for eclipse") |
324 | 415 |
325 user_config = params.get('generator_flags', {}).get('config', None) | 416 user_config = params.get('generator_flags', {}).get('config', None) |
326 if user_config: | 417 if user_config: |
327 GenerateOutputForConfig(target_list, target_dicts, data, params, | 418 GenerateOutputForConfig(target_list, target_dicts, data, params, |
328 user_config) | 419 user_config) |
329 else: | 420 else: |
330 config_names = target_dicts[target_list[0]]['configurations'].keys() | 421 config_names = target_dicts[target_list[0]]['configurations'].keys() |
331 for config_name in config_names: | 422 for config_name in config_names: |
332 GenerateOutputForConfig(target_list, target_dicts, data, params, | 423 GenerateOutputForConfig(target_list, target_dicts, data, params, |
333 config_name) | 424 config_name) |
334 | 425 |
OLD | NEW |