Developing IntelliJ Plugins – build tools Skip to content →

Developing IntelliJ Plugins – build tools

This post is the second part of the series dedicated to developing plugins for IntelliJ IDEA. Its main topic is a workflow for developing two interdependent plugins. Everything described here assumes that you have configured the workspace using the intellij-gradle-plugin.

Let’s imagine that you have two plugins – A and B. Plugin A provides an API and a common layer for plugin B. Plugin B consists of required implementations, and uses parts of the common layer, e.g. an API for displaying a dialog.

Both plugins have separate source code. Moreover, they have unmergeable build configurations (Gradle based). It means that it is impossible (or very time-consuming) to combine these configurations to build the plugins at one go. Because of that, they have to be built and deployed separately.

Sandbox requirements

When working with interdependent plugins, the sandbox requires that the common plugin (plugin A) be bundled into the installation. You can meet this requirement by copying the plugin libraries into the <path to the IntelliJ>/plugins/<plugin name> directory.

This causes the biggest challenge to overcome. If you want to develop both plugins simultaneously, they have to be deployed in different locations. Plugin A has to be copied to the directory mentioned above, whereas plugin B has to be deployed in the sandbox runtime location.

Deploying the plugins

In order to automate this flow as much as possible, you can take advantage of the custom Gradle tasks. This gives you a possibility to build the latest version of the plugin when running the sandbox.

Plugin B is built and deployed as the sandbox is starting. The Gradle plugin takes care of it. It is worth mentioning that plugin A has to be deployed before the sandbox starts working.

Configuring Gradle builds

Gradle properties

To make the configurations simpler, let’s define two properties. They can be set in two different ways – in the gradle.properties file, or with the –P flag defined when running a task from the command line.

The first property, intellijLocation, points to the location where IntelliJ is installed (the distribution used by the sandbox). The other property, sandboxRuntimeLocation, provides the information where the sandbox runtime files will be placed.

Plugin B

Let’s start with the plugin B configuration, since this plugin is easier to configure. It’s all about the gradle-intellij-plugin configuration.

plugins – the list of plugins the plugin depends on

localPath – the path to the locally installed IDEA distribution that should be used as a dependency

Plugin A

Configuration of plugin A is independent of the intellij-gradle-plugin. In this case, you will be working only with plain jar files. The only thing you have to do is to add a task that will be responsible for deploying the plugin jars in the configured location.

From now on, in order to refresh the plugin for IntelliJ location, you have to run the following command:

Tips & tricks

Incremental build

The proposed solution requires running two separate tasks to refresh both plugins. You have to deploy plugin A and then start the sandbox. And here you can take advantage of Gradle’s incremental build support. You can start the build with the –t flag. It will detect every change in the source code and re-deploy the plugin.

Plugin B repositories

When importing the plugin project into IntelliJ, the IDE will resolve Gradle dependencies using the cache. This may cause many compilation problems, e.g. if you added a class, used it in the plugin B’s code, but the cache hasn’t been refreshed. In the Gradle build, locations of repositories might be configured manually. This gives you an ability to force IntelliJ to use particular jars when building the plugin project.

Remember to configure plugin A dependencies for two environments – the local setup and the build server.

Configuring sources for dependencies

If you want to open a class from plugin A when developing plugin B, IntelliJ will most probably decompile the class from the jar that is added as a library to the project configuration. Fortunately, you can point to the location of the sources of the jar on the disc. To do so, go to Project Structure > Libraries and add sources for the selected library.

Thanks to this feature, you will be able to edit plugin A sources.

 

At first sight, such a setup seems to be very complicated, but in fact, it allows you to flexibly configure even the weirdest cases. Everything is only a matter of architecture and automation. I have used this workflow for about a month, and I haven’t encountered any problems that would take longer than 10 minutes to solve. For me, it works perfectly.