Intellij IDEA support for buildr

This extension provides tasks to generate Intellij IDEA project files by issuing:

$ buildr iidea:generate

This task will generate an .iml file for every project (or sub-project) and a .ipr that you can directly open for the root project.

The generated project files can be removed by issuing;

$ buildr iidea:clean

The iidea task generates the project files based on the settings of each project and extension specific settings. The main and test source trees are added to the .iml file for each project as are the respective resource directories. The target and report directories are excluded from the project. If the project files exist on the file system the extension will replace specific component sections in the xml with the generated component configurations.

Dependencies come in two forms. Dependencies on other projects and dependencies on external jars. Dependencies on other projects are added as module dependencies in the .iml while jars are added as regular file dependencies. Dependencies are exported from the .iml file if they are compile dependencies. If a artifact that matches dependency but has a classifier of ‘sources’ is present then it is configured as the source for the dependency. Note: Use “buildr artifacts:sources” to download the source for dependencies.

Installation

The extension is packaged as a gem named “buildr-iidea”, consult the ruby gems installation steps but typically it is either

sudo gem install buildr-iidea

for MRI ruby or

jgem install buildr-iidea

for jruby.

The user then needs to add the following require into the build file:

require 'buildr_iidea'

Warning

The iidea task may generate project files that conflict with the files generated by the built-in ‘idea’ and ‘idea7x’ tasks. As a result the built-in tasks would fail if ran while the extension is present. To avoid this problem the extension removes these tasks.

Idea Specific Directives

The extension specific settings of sub-projects inherit the parent projects settings unless overwritten.

Project file naming

The extension will use the last element of the projects name when generating the .ipr and .iml files. i.e. A project named “foo” will generate “foo.iml” and “foo.ipr” while a project named “foo:bar” will generate “bar/bar.iml” and no ipr. (The .ipr project files are only generated for base project). The name can be modified by setting the “ipr.suffix” or “iml.suffix” settings which specifies the suffix appended to the file names. The user can also override the name completely by setting “ipr.id” or “iml.id”.

Example: Setting id

define "foo" do
  ipr.id = "beep"
  define "bar" do
    iml.id = "baz"
  end
end

Will generate:

beep.ipr
foo.iml
bar/baz.iml

Example: Setting suffix

define "foo" do
  ipr.suffix = "-suffix1"
  iml.suffix = "-suffix2"
  define "bar"
end

Will generate:

foo-suffix1.ipr
foo-suffix2.iml
bar/bar-suffix2.iml

Disabling project file generation

The extension will not generate an iml file for a project if the “project.no_iml” method is invoked. Generation of ipr files can be disabled by invoking the method “project.no_ipr”.

Example

define "foo" do
  project.no_ipr
  define "bar" do
    project.no_iml
  end
end

Will generate:

foo.iml

Disabling generation of content section in .iml file

The extension will not generate a content section in an iml file if the “iml.skip_content!” method is invoked. This can be useful if a project is just exporting dependencies and has no associated source code. This may also be of use in scenarios where the build is repackaging an existing jar with more meta-data or the project is just a container for other projects.

Example

define "foo" do
  iml.skip_content!
end

VCS Integration

The extension will attempt to guess the VCS type of the project by looking for a .svn or .git directory in the base projects directory. If either of these are set it will configure the component as appropriate. Otherwise the user will need to manually specify the project to one of either ‘Git’ or ‘svn’ using the ipr.vcs setting.

Example

define "foo" do
  ipr.vcs = 'Git'
end

Dependency generation

A file dependency that exists in the local maven 2 repository is stored in the IML file relative to the $MAVEN_REPOSITORY$ environment variable (that defaults to ~/.m2/repository). The user can override the environment variable by setting the “iml.local_repository_env_override” setting. If the dependency does not exist in to maven repository or the “iml.local_repository_env_override” setting is set to nil, then the path stored in the IML is relative to the IML file.

Example: Setting local_repository_env_override

define "foo" do
  iml.local_repository_env_override = nil
  compile.with 'group:id:jar:1.0'
end

Will generate a dependency with a path like:

jar:///home/peter/.m2/repository/group/id/1.0/id-1.0.jar!/

rather than the default

jar://$MAVEN_REPOSITORY$/group/id/1.0/id-1.0.jar!/

Example: A dependency outside the maven repository

define "foo" do
  compile.with _("foos-dep.jar")
end

Will generate a dependency with a path like:

jar://$MODULE_DIR$/foo-dep.jar!/

Module Facets

Facets are IDEAs mechanism for adding support for languages, tools and frameworks other than core java. A facet can be added to a project so that it can be deployed as a web application or a hibernate application. A facet can also be used t provide support for other languages such as ruby and scala. The extension makes it possible to generate .iml with the appropriate facets via the “iml.add_facet” method. It should be noted that facets are NOT inherited by sub-projects.

Example

This example adds the web facet to a project.

define "foo" do
  iml.add_facet("Web","web") do |facet|
    facet.configuration do |conf|
      conf.descriptors do |desc|
        desc.deploymentDescriptor :name => 'web.xml',
          :url => "file://$MODULE_DIR$/src/main/webapp/WEB-INF/web.xml",
          :optional => "false", :version => "2.4"
      end
      conf.webroots do |webroots|
        webroots.root :url => "file://$MODULE_DIR$/src/main/webapp", :relative => "/"
      end
    end
  end
end

Custom Component Sections

If the extension does not provide capability to generate configuration for a particular IDEA plugin the user can provide their own configuration data via the “ipr.add_component” or “iml.add_component” methods.

Example: Adding .ipr specific component

This example changes the compiler configuration for project.

define "foo" do
  ipr.add_component("CompilerConfiguration") do |component|
    component.option :name => 'DEFAULT_COMPILER', :value => 'Javac'
    component.option :name => 'DEPLOY_AFTER_MAKE', :value => '0'
    component.resourceExtensions do |xml|
      xml.entry :name => '.+\.nonexistent'
    end
    component.wildcardResourceExtensions do |xml|
      xml.entry :name => '?*.nonexistent'
    end
  end
end

Example: Adding .iml specific component

This example adds the web facet to a project. Note: This overrides the facets defined by the “iml.add_facet” method.

define "foo" do
  iml.add_component("FacetManager") do |component|
    component.facet :type => 'web', :name => 'Web' do |facet|
      facet.configuration do |conf|
        conf.descriptors do |desc|
          desc.deploymentDescriptor :name => 'web.xml',
            :url => "file://$MODULE_DIR$/src/main/webapp/WEB-INF/web.xml",
            :optional => "false", :version => "2.4"
        end
        conf.webroots do |webroots|
          webroots.root :url => "file://$MODULE_DIR$/src/main/webapp", :relative => "/"
        end
      end
    end
  end
end

Templates

The underlying project files are xml the contain elements for a number of “components”. The extension will load any existing project files and replace or add any component elements that are generated by the extension. The extension also allows the user to specify a template with either ipr.template or iml.template settings. If a template is specified it will be loaded and any component elements in these documents will be merged into the base document prior to merging in generated sections. Templates are useful if you want to enforce certain configuration options (i.e. project specific code style).

Example

define "foo" do
  ipr.template = 'project.ipr.template'
  iml.template = 'module.ipr.template'
end

Groups

IDEA provides the facility to organise modules into groups. By default the extension does not do this but it can be enabled by iml.group setting. If that setting is set to true then the .iml file will be placed in a group based on the parent projects name. If the setting is a string then that is used as the name of the group.

Example

define "foo" do
  iml.group = true
  define 'bar' do
    define 'baz'
  end
  define 'rab' do
    iml.group = "MyGroup"
  end
end

Will place the generated .imls in the following groups:

foo.iml                => ''
bar/bar.iml            => 'foo'
bar/baz/baz.iml        => 'foo/bar'
rab/rab.iml            => 'MyGroup'

Add Extra .iml files to .ipr

The ‘ipr.extra_modules’ setting makes it possible to add extra modules to the generated iml file. The setting is an array of file names relative to the base project directory.

Example

define "foo" do
  ipr.extra_modules << 'other.iml'
  ipr.extra_modules << 'other_other.iml'
end

Will add the ‘other.iml’ and ‘other_other.iml’ files to the .ipr project files.

Compatibility

It’s been tested with IDEA 9.x.

Future Work

The following is a list of feature requests for future versions of the extension. Feel free to jump in and supply a patch if you have gone ahead and implemented the feature.

Auto-generate Web Facet

Any project that defines a war package should have a web facet auto-generated for it.

Auto-generate Scala Facet

Any project that contains scala source code should have a scala facet autogenerated for it.

Support generation of IDEA files into separate hierarchy

Add the ability for the module and project files to be generated off into a different directory hierarchy. i.e. Generate project modules into .idea_project in projects base directory.

Fix tests to work when installed as a gem

The library should determine whether it is installed as a gem and if so use the gem to invoke the the tests. See rubygems.rubyforge.org/rubygems-update/Gem.html#M000228 for a possible interface to determine if currently installed as a gem.