How to specify library dependencies for an IntelliJ IDEA plugin? - intellij-idea

I am developing a plugin for IntelliJ IDEA. The way I am going about this is by creating a plugin project in IDEA, then packaging this into a jar with appropriate META-INF/plugin.xml, and installing the plugin from the jar.
The problem is that I would like to add a dependency on org.scala-lang:scala-library:2.11.0. I have this specified as a library dependency in the IDEA project, but this information never seems to get passed along to the generated JAR.
How can I include this information in such a way that IntelliJ IDEA will recognize it?

As far as I understand, you want to bundle some library (e.g. scala library) with your plugin.
This is pretty simple.
Go to Project Settings, select module and go to Dependencies tab. Set scope for the library you want to bundle to 'Compile'. In this example it is 'checker-framework' library. 'groovy-2.3.6' library will not be bundled due to its scope set to 'Provided'. Save changes.
Prepare plugin for deployment
Then you got plugin, zipped, ready for deployment (uploading to repo or installing locally) in the root of project. It will contain lib folder with all necessary jars.

The officially supported plugin dependency management solution is to use Gradle with the gradle-intellij-plugin, via Gradle's dependencies or the intellij.plugins entry points. If you want to add a dependency on an artifact (ex. hosted on Maven Central), then configure dependencies just as you normally would in a Gradle based project:
buildscript {
repositories {
mavenCentral()
}
}
dependencies {
compile("org.scala-lang:scala-library:2.11.0")
}
The intellij.plugins entry point will add an artifact in the current project, as well as a <depends> tag to your plugin.xml file. To install an external plugin alongside your own, for example if you are using the Plugin Extensions feature (suppose the plugin is hosted on the JetBrains Plugin Repository), use the following snippet:
plugins {
id "org.jetbrains.intellij" version "0.2.13"
}
intellij {
//...
plugins "org.intellij.scala:2017.2.638"
}

Related

How do I encapsulate version management for gradle plugins?

Problem
I have a setup of various distinct repos/projects (i.e. app1, app2, app3) that all depend on shared functionality in my base package.
The projects also use various other third-party dependencies (i.e. app1 and app3 use spring, all of them use kotlinx-serialization).
I want to synchronise the versions of all third-party dependencies, so that any project using my base package uses the same version of every third-party dependency. However, I don't want to introduce new dependencies to projects that do not use them (i.e. app2 does not use spring)
Solution attempts
For libraries, I have been able to solve this with the help of a gradle platform, which does exactly what I want - I specify the versions in my base package, then add the platform as a dependency to my projects and can then simply add dependencies by name (i.e. implementation("org.springframework.boot:some-package")) without having to specify a version number, because it uses the provided value from my platform.
However, for plugins, I have not been able to do this. Many libraries come with plugins and naturally the plugin should be at the same version as the library. I have tried various approaches, including writing a standalone plugin, but none have worked.
Current best idea
I added implementation("org.springframework.boot:spring-boot-gradle-plugin:3.0.2") to the dependencies of my standalone plugin. Then, I added the following code to my standalone plugin:
class BasePlugin : Plugin<Project> {
override fun apply(target: Project) {
target.plugins.apply("org.springframework.boot")
}
}
This works and applies the plugin to my main project at the correct version. However, there are 2 major problems with this:
a) Now every project applies the spring plugin, including app2 (which does not use spring).
b) I have many plugins to manage and no idea how to get the long implementation-string for most of them. I found the "org.springframework.boot:spring-boot-gradle-plugin:3.0.2" by looking up the plugin-id on https://plugins.gradle.org/ and then looking at the legacy plugin application section, which sounds like I am on the wrong track.
I just want to manage the versions of plugins and libraries of multiple projects/repos in a central place - this feels like a fairly basic use case - why is this so hard?
There are some great and detailed answers about dependency management, but unfortunately none worked to perform cross-project version management for plugins.
It seems that there is no gradle functionality to do this, but I got it working with a bit of a workaround. Here is my (working) approach, in hope that it helps someone else with this:
Create a Standalone gradle Plugin
In the build.gradle.kts of the plugin, include the maven coordinates (not its ID) of every other plugin whose version you want to manage in any of your projects in the dependency block with the api keyword. i.e. api("org.springframework:spring-web:6.0.2")
In the main projects, remove every other plugin from the plugins block, so that your custom standalone plugin is the only one remaining.
Create a file (i.e. a plugins.json or whatever you want) in the project root directory of all main projects and in there supply the plugin IDs of the plugins that you actually intend to use in that project. Just the IDs, no version numbers, i.e. "org.springframework.boot" for Spring's plugin. (Keep in mind that for plugins declared as kotlin("abc") you will have to add the prefix "org.jetbrains.com.", as the kotlin method is just syntactic sugar for that)
In your plugin source code, in the overriden apply method, look for. a file named plugins.json (or whatever you chose) in the project.buildFile.parent directory (which will be the directory of the project using this plugin, NOT of the plugin itself). From this file, read the plugin IDs
for every pluginID in the file, call project.plugins.apply(id)
How/Why it works:
The main project build.gradle.kts is executed, looks at the plugin block and applies your standalone plugin (which is the only one), which calls its apply method.
This plugin then applies other plugins based on their ID from the file.
Normally, this will throw an error because these plugins are not found, but because we defined them as dependencies with the api keyword in our standalone plugin, they are now available on the classpath and in exactly the version of that import statement.
Hope it helps someone!
I use version numbers in a gradle.properties file for this purpose. Since the introduction of Gradle version catalogs, my approach is probably a bit out of date, but I'll share it here anyway. It's based on the fact that plugin versions can be managed in settings.gradle.kts by reading values from the properties file.
In gradle.properties:
springBootVersion=3.0.2
In settings.gradle.kts:
pluginManagement {
val springBootVersion: String by settings
plugins {
id("org.springframework.boot") version springBootVersion
}
}
And finally in build.gradle.kts:
plugins {
id("org.springframework.boot")
}
dependencies {
val springBootVersion: String by project
implementation(platform("org.springframework.boot:spring-boot-dependencies:$springBootVersion"))
}
Notice that the plugin version is omitted in the build script because it is already specified in the settings file.
And note also that the method for accessing the property in the settings script is slightly different from that in the build script.
a) Now every project applies the spring plugin, including app2 (which does not use spring).
It is indeed better to avoid applying too many plugins - and that's why Gradle encourages reacting to plugins.
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.*
import org.springframework.boot.gradle.plugin.SpringBootPlugin
class BasePlugin : Plugin<Project> {
override fun apply(target: Project) {
// don't apply
//target.plugins.apply("org.springframework.boot")
// instead, react!
target.plugins.withType<SpringBootPlugin>().configureEach {
// this configuration will only trigger if the project applies both
// BasePlugin *and* the Spring Boot pluging
}
// you can also react based on the plugin ID
target.pluginManager.withPlugin("org.springframework.boot") {
}
}
}
Using the class is convenient if you want to access the plugin, or the plugin's extension, in a typesafe manner.
You can find the Plugin's class by
looking in the source code for the class that implements Plugin<Project>,
in the plugin's build config for the implementationClass,
or in the published plugin JAR - in the META-INF/gradle-plugins directory there will be a file that has the implementationClass.
This doesn't help your version alignment problem - but I thought it was worth mentioning!
b) I have many plugins to manage and no idea how to get the long implementation-string for most of them. I found the "org.springframework.boot:spring-boot-gradle-plugin:3.0.2" by looking up the plugin-id on https://plugins.gradle.org/ and then looking at the legacy plugin application section, which sounds like I am on the wrong track.
You're on the right track with the "long implementation string" as you call it. I'll refer to those as the 'Maven coordinates' of the plugin.
Gradle Plugin Maven Coordinates
The plugin id of the Kotlin JVM plugin is org.jetbrains.kotlin.jvm, but the Maven coordinates are org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0 .
The 'legacy' part refers to how the plugins are applied, using the apply(plugin = "...") syntax. The new way uses the plugin {} block, but under the hood, both methods still use the Maven coordinates of the plugin.
If you add those Maven coordinates (with versions) to your Java Platform, then you can import the platform into your project. But where?
Defining plugin versions
There are a lot of ways to define plugins, so I'll only describe one, and coincidentally it will be compatible with defining the version using a Java Platform.
If you're familiar with buildSrc convention plugins, you'll know that they can apply plugins, but they can't define versions.
// ./buildSrc/src/main/kotlin/kotlin-jvm-convention.gradle.kts
plugins {
kotlin("jvm") version "1.8.0" // error: pre-compiled script plugins can't set plugin versions!
}
Instead, plugin versions must be defined in the build config for buildSrc
// ./buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
// the Maven coordinates of the Kotlin JVM plugin - including the version
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0")
}
This looks a lot more traditional, and so I hope the next step is clean: use your Java Platform!
Applying a Java Platform to buildSrc
// ./buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
// import your Java Platform
implementation(platform("my.group:my-platform:1.2.3"))
// no version necessary - it will be supplied by my.group:my-platform
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin")
}
Note that this same method will also apply if your projects an 'included build' instead of buildSrc.
Once the plugin versions are defined in ./buildSrc/build.gradle.kts, you can use them throughout your project (whether in convention plugins, or in subprojects), they will be aligned.
// ./subproject-alpha/build.gradle.kts
plugins {
kotlin("jvm") // no version here - it's defined in buildSrc/build.gradle.kts
}

Package local jar classes in kotlin jvm jar with gradle

I'm using the Kotlin jvm Gradle plugin to build and publish a Java/JVM jar. With just the Kotlin source, everything works. However, this particular project references some local jars as dependencies. I would like to grab the class files from those local jars and include them in my jar file for publishing.
I've found a few examples but they're all fairly old, or use groovy, or are building a separate jar that's not being published.
This is specifically for a gradle plugin, but I assume it would be the same for any JVM build. The relevant part of the build.gradle.kts is as follows:
plugins {
id("java-gradle-plugin")
kotlin("jvm")
}
dependencies {
implementation(kotlin("gradle-plugin-api"))
implementation(fileTree("lib") { include("*.jar") })
}
There are jar files in the lib folder, and I'd just like to pull the classes from those files into the jar we're going to publish with this plugin (the source jars come from open source with a compatible license, but they're not published anywhere we can reference from gradle).
To preempt "why don't you do ___" answers, this is to get something out the door quickly. If this is the route we're taking long term, we'll try to collect and build the source from the dependency projects, but it's a complex config.

Intellij: Cannot select "Groovy" in "Add Framework Support"

I would like to add Groovy support to my project in Intellij Idea 2018.1. But when I open "Add Framework Support..." from the project context menu, I don't see "Groovy" in the list of frameworks:
I have Groovy installed on my machine and configured it as a global library in in IntelliJ:
Not sure whether this should be possible, but I also cannot add Groovy as an SDK:
I added Groovy as a module dependency:
My underlying problem is that IntelliJ does not properly recognize Groovy code, e.g. I get
Cannot resolve symbol `String`
when I use the String class:
Any hints on how I can make Groovy available as a Framework and an SDK?
Solution
Thanks to CrazyCoder pointing me to the broken JDK. Fixing the JDK and adding Groovy as a module dependency fixed the problem. See https://www.jetbrains.com/help/idea/creating-and-managing-modules.html
Make sure your JDK configuration is correct. As #crazycoder commented, OP's is red indicating the path might be incorrect.
Groovy can be added as a module or library in Intellij or dependency in its dependency file (pom.xml, build.gradle etc)
1. Module (in Intellij):
File > Project Structure Ctrl+Alt+Shift+S > click Modules > Dependencies.
2. Library (in Intellij):
Go to File > Project Structure... > Libraries > Click on the plus sign on the leftmost side > Add the Groovy jar or maven source at your choice
3. Dependency (build.gradle):
group 'com.test'
version '1.0.0'
apply plugin: 'groovy'
repositories {
mavenCentral()
}
dependencies {
implementation "org.codehaus.groovy:groovy:2.4.5"
}

Intellij Plugin Development - Gradle does not find dependency of IntelliJ

I am currently developing a plugin for IntelliJ and trying to use another built-in IntelliJ Plugin as dependency (git4idea). As described in the IntelliJ Plugin Development documentation, I added the required JARs to my class Path in Project Structure:
Project Structure Dialog Screenshot
I also added <depends>Git4Idea</depends> to my plugin.xml file.
IntelliJ finds these jars now and Code Completion works well, no errors found...
But when I try to build the Plugin with gradle I get ClassNotFound errors or errors like this:
TkGitflowBaseImpl.java:15: error: package git4idea.commands does not exist
import git4idea.commands.Git;
^
Obviously, Gradle does not find these jars. Since they are part of the IntelliJ installation, I can't just add them to a lib folder and add them as local jars in the build.gradle file. As Gradle JVM, I chose the exact same JVM I chose as JVM behind the IDEA Platform SDK, so the jars should be available to Gradle.
Do you know how I can help Gradle find these jars or add them as "provided" dependencies without adding them to a lib folder?
I am using IntelliJ IDEA 2017.2.5 and Gradle 4.2.1
After reading through the documentation of the IntelliJ Gradle Plugin (https://github.com/JetBrains/gradle-intellij-plugin), I saw that that a "=" was missing in the build.gradle file like:
intellij {
version '2017.2.5'
pluginName 'pluginname'
plugins = ['Git4Idea']
}
instead of
intellij {
version '2017.2.5'
pluginName 'pluginname'
plugins ['Git4Idea']
}
https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html#optional-plugin-dependencies
build.gradle
intellij {
version = '2021.2'
plugins = ['Git4Idea']
}
src\main\resources\META-INF\plugin.xml
<depends>Git4Idea</depends>

How to attach sources to auto-generated Gradle-based dependencies in IntelliJ IDEA 13.0 in a way that will survive next Gradle projects refresh?

Is there a simple way to attach sources to auto-generated Gradle-based dependencies with IntelliJ IDEA 13.0 that won't be erased on next Gradle refresh?
For example, my build.gradle has such entry:
project(":projectName") {
dependencies {
compile files("c:/Program Files (x86)/groovy-2.2.1/embeddable/groovy-all-2.2.1.jar")
// more stuff here
}
}
Thus when I click Refresh all Gradle projects
I get a nice dependency set looking like so:
but there are no sources attached and if I do attach them manually, on next refresh they are erased.
I have sources for many different libraries, sometimes in jar file, sometimes directly in the file system (e.g. my groovy install has sources in c:\Program Files (x86)\groovy-2.2.1\src\).
Some of the dependencies I use can be downloaded from maven central repo, but in my build.gradle all the dependencies are configured to be taken from my local file system.
Thanks!
Konrad
The only easy solution is to get the dependencies straight from a Maven repository (either Maven Central or an inhouse repository). If that's not an option for you, you'll have to configure sources via a hook such as idea.module.iml.withXml or idea.module.iml.whenMerged (after applying the idea plugin to allprojects). You can find details on these APIs in the Gradle Build Language Reference and the Gradle User Guide.