"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/node-gyp/gyp/pylib/gyp/generator/eclipse.py" (8 Mar 2017, 17014 Bytes) of archive /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

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