def write_modules()

in src/com/facebook/buck/command/intellij.py [0:0]


def write_modules(modules):
  """Writes one XML file for each module."""
  for module in modules:
    # Build up the XML.
    module_type = 'JAVA_MODULE'
    if 'isIntelliJPlugin' in module and module['isIntelliJPlugin']:
      module_type = 'PLUGIN_MODULE'

    xml = MODULE_XML_START % {
      'type': module_type,
    }

    # Android facet, if appropriate.
    if module.get('hasAndroidFacet') == True:
      if 'keystorePath' in module:
        keystore = 'file://$MODULE_DIR$/%s' % module['keystorePath']
      else:
        keystore = ''

      if 'androidManifest' in module:
        android_manifest = module['androidManifest']
      else:
        android_manifest = '/AndroidManifest.xml'

      is_library_project = module['isAndroidLibraryProject']
      android_params = {
        'android_manifest': android_manifest,
        'res': '/res',
        'is_android_library_project': str(is_library_project).lower(),
        'run_proguard': 'false',
        'module_gen_path': module['moduleGenPath'],
        'proguard_config': '/proguard.cfg',
        'keystore': keystore,
        'libs_path' : '/%s' % module.get('nativeLibs', 'libs'),
      }
      xml += ANDROID_FACET % android_params

    # Source code and libraries component.
    xml += '\n  <component name="NewModuleRootManager" inherit-compiler-output="true">'

    # Empirically, if there are multiple source folders, then the <content> element for the
    # buck-out/android/gen folder should be listed before the other source folders.
    num_source_folders = len(module['sourceFolders'])
    if num_source_folders > 1:
      xml = add_buck_android_source_folder(xml, module)

    # Source folders.
    xml += '\n    <content url="file://$MODULE_DIR$">'
    for source_folder in module['sourceFolders']:
      if 'packagePrefix' in source_folder:
        package_prefix = 'packagePrefix="%s" ' % source_folder['packagePrefix']
      else:
        package_prefix = ''
      xml += '\n      <sourceFolder url="%(url)s" isTestSource="%(is_test_source)s" %(package_prefix)s/>' % {
               'url': source_folder['url'],
               'is_test_source': str(source_folder['isTestSource']).lower(),
               'package_prefix': package_prefix
             }
    for exclude_folder in module['excludeFolders']:
      xml += '\n      <excludeFolder url="%s" />' % exclude_folder['url']
    xml += '\n    </content>'

    xml = add_annotation_generated_source_folder(xml, module)

    # Empirically, if there is one source folder, then the <content> element for the
    # buck-out/android/gen folder should be listed after the other source folders.
    if num_source_folders <= 1:
      xml = add_buck_android_source_folder(xml, module)

    # Dependencies.
    dependencies = module['dependencies']
    module_name = module['name']

    # We need to filter out some of the modules in the dependency list:
    # (1) The module may list itself as a dependency with scope="TEST", which is bad.
    # (2) The module may list another module as a dependency with both COMPILE and TEST scopes, in
    #     which case the COMPILE scope should win.

    # compile_dependencies will be the set of names of dependent modules that do not have scope="TEST"
    compile_dependencies = filter(lambda dep: dep['type'] == 'module' and
        ((not ('scope' in dep)) or dep['scope'] != 'TEST'),
        dependencies)
    compile_dependencies = map(lambda dep: dep['moduleName'], compile_dependencies)
    compile_dependencies = set(compile_dependencies)

    # Filter dependencies to satisfy (1) and (2) defined above.
    filtered_dependencies = []
    for dep in dependencies:
      if dep['type'] != 'module':
        # Non-module dependencies should still be included.
        filtered_dependencies.append(dep)
      else:
        # dep must be a module
        dep_module_name = dep['moduleName']
        if dep_module_name == module_name:
          # Exclude self-references!
          continue
        elif 'scope' in dep and dep['scope'] == 'TEST':
          # If this is a scope="TEST" module and the module is going to be included as
          # a scope="COMPILE" module, then exclude it.
          if not (dep_module_name in compile_dependencies):
            filtered_dependencies.append(dep)
        else:
          # Non-test modules should still be included.
          filtered_dependencies.append(dep)

    # Now that we have filtered the dependencies, we can convert the remaining ones directly into
    # XML.
    excluded_deps_names = set()

    if module_type == 'PLUGIN_MODULE':
      # all the jars below are parts of IntelliJ SDK and even though they are required
      # for language plugins to work standalone, they cannot be included as the plugin
      # module dependency because they would clash with IntelliJ
      excluded_deps_names = set([
        'annotations',    # org/intellij/lang/annotations, org/jetbrains/annotations
        'extensions',     # com/intellij/openapi/extensions/
        'idea',           # org/intellij, com/intellij
        'jdom',           # org/jdom
        'junit',          # junit/
        'light_psi_all',  # light psi library
        'openapi',        # com/intellij/openapi
        'picocontainer',  # org/picocontainer
        'trove4j',        # gnu/trove
        'util',           # com/intellij/util
      ])

    for dep in filtered_dependencies:
      if 'scope' in dep:
        dep_scope = 'scope="%s" ' % dep['scope']
      else:
        dep_scope = ''

      dep_type = dep['type']
      if dep_type == 'library':
        if dep['name'] in excluded_deps_names:
          continue

        xml += '\n    <orderEntry type="library" exported="" %sname="%s" level="project" />' % (dep_scope, dep['name'])
      elif dep_type == 'module':
        dep_module_name = dep['moduleName']

        # TODO(mbolin): Eliminate this special-case for jackson. It exists because jackson is not
        # an ordinary module: it is a module that functions as a library. Project.java should add it
        # as such in project.json to eliminate this special case.
        if dep_module_name == 'module_first_party_orca_third_party_jackson':
          exported = 'exported="" '
        else:
          exported = ''
        xml += '\n    <orderEntry type="module" module-name="%s" %s%s/>' % (dep_module_name, exported, dep_scope)
      elif dep_type == 'inheritedJdk':
        xml += '\n    <orderEntry type="inheritedJdk" />'
      elif dep_type == 'jdk':
        xml += '\n    <orderEntry type="jdk" jdkName="%s" jdkType="%s" />' % (dep['jdkName'], dep['jdkType'])
      elif dep_type == 'sourceFolder':
        xml += '\n    <orderEntry type="sourceFolder" forTests="false" />'

    # Close source code and libraries component.
    xml += '\n  </component>'

    # Close XML.
    xml += MODULE_XML_END

    # Write the module to a file.
    write_file_if_changed(module['pathToImlFile'], xml)