Gradle: add plugin dependency from another plugin - gradle

I'm creating gradle custom plugin and one of my tasks needs to be sure that another plugin applied to same project. Because it will operate on top of it.
I want for users of my plugin to avoid setting up an explicit dependency to another plugin - I want to do it inside my plugin.
So, I want to have this plugin (https://plugins.gradle.org/plugin/org.hidetake.ssh) applied. It's my dependency.
The way how I create plugin - I just create a class code on groovy, put it in buildSrc\src\main\groovy and apply groovy plugin in project. So my custom plugin is visible to gradle on build phase. It works, I have few other plugins done this way for same project, so it's fine for now.
I've looked through other topics and google for same question, but I can not make this work for me. This how I apply the code:
void apply(Project project) {
project.buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "org.hidetake:gradle-ssh-plugin:1.1.3"
}
}
project.apply plugin: "org.hidetake.ssh"
...
The error message I got: Plugin with id 'org.hidetake.ssh' not found.
I tried to do it via gradle api also using project.repositories.mavenCentral() and project.dependencies.add and project.apply(plugin:'org.hidetake.ssh') then - doesn't work also - same error message. Tried to use long notation in project.dependencies.add("myConfig",[group:'org.hidetake', name:'gradle-ssh-plugin', version:'1.1.3']) - no result.
Appreciate if someone can guide to the correct syntax\way to make it work.

Ok, finally I got it. To solve the issue you need to do the following:
Place build.gradle in your buildSrc directory.
Declare dependency for the plugin as runtime. Like this:
repositories {
jcenter()
}
dependencies {
runtime 'org.hidetake:gradle-ssh-plugin:2.6.0'
}
Apply plugin explicitly in your own plugin definition. Like this:
void apply(Project project) {
project.pluginManager.apply('org.hidetake.ssh')
...

Related

Gradle: Use a sibling subproject as plugin

I have a project with two subprobjects: gradle-plugin and plugin-consumer. I want to apply the plugin from gradle-plugin to plugin-consumer. I tried to do this:
// plugin-consumer/build.gradle
buildscript {
dependencies {
classpath project(':gradle-plugin')
}
}
apply plugin: 'my.plugin.id'
But I was greeted with the following error:
A problem occurred configuring project ':plugin-consumer'.
> Cannot use project dependencies in a script classpath definition.
I assume this is not supported because it'd require fully building gradle-plugin before plugin-consumer can be configured.
Fortunately I can use a fileTree dependency to accomplish my goal:
// plugin-consumer/build.gradle
buildscript {
dependencies {
classpath fileTree(includes: ['*.jar'], dir: '../gradle-plugin/build/libs')
}
}
apply plugin: 'my.plugin.id'
This works, but it feels like a massive hack and leads to "bootstrapping problems".
For example, I can't clean gradle-plugin because the (deleted) jar file is necessary for configuring plugin-consumer, which must be done to (re)build gradle-plugin.
Fortunately this can be avoided by always running build immediately after clean (in the same 'run' so to speak). This can be done manually (gradle clean build) or automatically (with clean.finalizedBy(build)). Again, this works, but feels like a hack.
At last, my actual question: is there a better way to do this?
Please note that gradle-plugin is an independent plugin that's not only used by plugin-consumer, therefore buildSrc is unfortunately not an appropriate solution here.
You can publish the plugin to your local Maven repository with the Maven Publish plugin. Then simply consume it like any other artifact.
Assuming you have something similar in your plugin project:
plugins {
`maven-publish`
`java-gradle-plugin`
}
Simply publish it locally:
./gradlew :my-plugin-project:publishToMavenLocal
Then in your consuming project, something like:
buildscript {
repositories {
mavenLocal()
}
dependencies {
"classpath"("com.example:my-plugin-gav:1.0.0-SNAPSHOT")
}
}
// apply plugin

Why do I get UnknownPluginException when trying to use a custom Kotlin complier plugin in Gradle?

I have created a custom Kotlin compiler plugin for Gradle. It was inspired by kotlin-allopen (2) and sample-kotlin-compiler-plugin, and is supposed to make all Kotlin classes non-final.
The problem is, I'm unable to use it in my projects, I only get the following:
Caused by: org.gradle.api.plugins.UnknownPluginException: Plugin with id 'no.synth.kotlin.plugins.kotlin-really-allopen' not found.
at org.gradle.api.internal.plugins.DefaultPluginManager.apply(DefaultPluginManager.java:131)
I have tried both the "new" plugin syntax:
plugins {
id "no.synth.kotlin.plugins.kotlin-really-allopen" version "0.1"
}
.. and the old one:
buildscript {
repositories {
mavenLocal()
}
dependencies {
classpath "no.synth.kotlin.plugins:kotlin-really-allopen:0.1"
}
}
apply plugin: "kotlin-really-allopen" // I've tried "no.synth.kotlin.plugins.kotlin-really-allopen" as well
So what am I doing wrong? Here's the plugin: https://github.com/henrik242/kotlin-really-allopen
EDIT: I have updated the repository with an example app and a README.md to easily reproduce the problem.
Your Gradle plugin doesn't seem to contain any entry under META-INF/gradle-plugins.
Gradle requires that every plugin ID is mapped to the implementation class, and this mapping is stored in META-INF/gradle-plugins resources.
To map the plugin ID kotlin-really-allopen, you would need a resource file
src/main/resources/META-INF/gradle-plugins/kotlin-really-allopen.properties.
See: Wiring for a custom plugin
You can also use the Gradle Plugin Development Plugin, which automatically generates these entries from the build script DSL.
Also, your repository doesn't seem to contain an actual Gradle plugin implementation, there's only the part that the compiler needs to load. For an example that contains the Gradle part too, take a look at kevinmost/debuglog.
Move apply plugin: "kotlin-really-allopen" in your build.gradle module app on top

Artifact id resolved wrong

I am new to gradle and I am facing a weird problem while I’m trying to add a plugin in gradle. I know that we have to specify an if and version in plugin body for a gradle build, but I tried to add a plugin with some id and version. My question is..how does a gradle build know which artifact id to choose if there are multiple artifacts under same group id? I know that this might be a lame question...but I’m pretty new to gradle and I’d like to know your input.
Are you trying to apply the spring-boot-plugin?
If so, does the project you're trying to apply the plugin to have a buildscript block like this:
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.4.RELEASE"
}
}
apply plugin: "org.springframework.boot"
or a plugin closure:
plugins {
id "org.springframework.boot" version "2.0.4.RELEASE"
}
With a buildscript block, Gradle knows where to find the plugin because I've specified a repository for it to go look for it. After resolving the dependency path it finds and downloads the plugin, then puts it on the classpath for use in build.gradle files. It then only needs to get applied, i.e. apply plugin: ....
With the plugins closure, things are a bit trickier. Plugins are published under a unique id, which is looked up and gradle resolves the specified version. I'm not too terribly knowledgeable about how this is done, but here, new plugin mechanism , describes some differences between buildscript {} apply plugin: ... and plugins {}.

How to share boilerplate Kotlin configuration across multiple Gradle projects?

The typical Kotlin configuration in a Gradle project is very boilerplate, and I'm looking for a way of abstracting it out into an external build script so that it can be reused.
I have a working solution (below), but it feels like a bit of a hack as the kotlin-gradle-plugin doesn't work out of the box this way.
It's messy to apply any non-standard plugin from an external script as you can't apply the plugin by id, i.e.
apply plugin: 'kotlin' will result in Plugin with id 'kotlin' not found.
The simple (well, usually) workaround is to apply by the fully qualified classname of the plugin, i.e.
apply plugin: org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
which in this case throws a nice little exception indicating that the plugin probably wasn't meant to be called this way:
Failed to determine source cofiguration of kotlin plugin.
Can not download core. Please verify that this or any parent project
contains 'kotlin-gradle-plugin' in buildscript's classpath configuration.
So I managed to hack together a plugin (just a modified version of the real plugin) which forces it to find the plugin from the current buildscript.
kotlin.gradle
buildscript {
ext.kotlin_version = "1.0.3"
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}
apply plugin: CustomKotlinPlugin
import org.jetbrains.kotlin.gradle.plugin.CleanUpBuildListener
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper
import org.jetbrains.kotlin.gradle.plugin.KotlinPlugin
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
/**
* Wrapper around the Kotlin plugin wrapper (this code is largely a refactoring of KotlinBasePluginWrapper).
* This is required because the default behaviour expects the kotlin plugin to be applied from the project,
* not from an external buildscript.
*/
class CustomKotlinPlugin extends KotlinBasePluginWrapper {
#Override
void apply(Project project) {
// use String literal as KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY constant isn't available
System.setProperty("kotlin.environment.keepalive", "true")
// just use the kotlin version defined in this script
project.extensions.extraProperties?.set("kotlin.gradle.plugin.version", project.property('kotlin_version'))
// get the plugin using the current buildscript
def plugin = getPlugin(this.class.classLoader, project.buildscript)
plugin.apply(project)
def cleanUpBuildListener = new CleanUpBuildListener(this.class.classLoader, project)
cleanUpBuildListener.buildStarted()
project.gradle.addBuildListener(cleanUpBuildListener)
}
#Override
Plugin<Project> getPlugin(ClassLoader pluginClassLoader, ScriptHandler scriptHandler){
return new KotlinPlugin(scriptHandler, new KotlinTasksProvider(pluginClassLoader));
}
}
This can then be applied in any project (i.e. apply from: "kotlin.gradle") and you're up and running for Kotlin development.
It works, and I haven't had any issues yet, but I'm wondering if there is a better way? I'm not really keen on merging in changes to the plugin every time there's a new version of Kotlin.
Check out the nebula-kotlin-plugin. It seems very close to what you're trying to achieve there.
The problem here is that there is a known gradle bug about the inability to apply plugins by id from init scripts. That's why you need to use fully qualified class name as a workaround.
E.g. I have the following in the init script and it works:
apply plugin: org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJvmPlugin
By the way, I created a gradle plugin for preparing custom gradle distributions with common setup defined in init script - custom-gradle-dist. It works perfectly for my projects, e.g. a build.gradle for a library project looks like this (this is a complete file, all repository, apply plugin, dependencies etc setup is defined in the init script):
dependencies {
compile 'org.springframework.kafka:spring-kafka'
}

Gradle plugins DSL: Restriction on declaration location

I have a few Gradle scripts that get applied via apply from: 'my-build.gradle'. If I use the new plugins DSL as follows in the external build file my-build.gradle, it fails with the following error:
> startup failed:
Only Project build scripts can contain plugins {} blocks
See http://gradle.org/docs/2.3/userguide/plugins.html#sec:plugins_block
for information on the plugins {} block
Looking at the documentation pointed in the error message didn't reveal as to why the restriction is in place. Why is there a restriction on the location of the plugins declaration?
Files for reference below.
my-build.gradle file:
plugins {
id "net.saliman.cobertura" version "2.2.5"
}
build.gradle file:
apply from: "my-build.gradle"
// Other stuff
This is how you can use plugins in external Gradle files such as your my-build.gradle:
buildscript {
repositories {
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath "org.sonarqube.gradle:gradle-sonarqube-plugin:1.1"
classpath "net.saliman:gradle-cobertura-plugin:2.2.8"
}
}
// Because this is a helper script that's sourced in from a build.gradle, we can't use the ID of external plugins
// We either use the full class name of the plugin without quotes or an init script: http://www.gradle.org/docs/current/userguide/init_scripts.html
apply plugin: org.sonarqube.gradle.SonarQubePlugin
apply plugin: net.saliman.gradle.plugin.cobertura.CoberturaPlugin
// rest of my-build.gradle omitted
Above I've activated the plugins for SonarQube and Cobertura.
Generally, to get the fully qualified class name of your plugin you will have to look inside its .jar file.
As for the technical reasons why you can't use a plugins {} block in an external file, I do not know. It might have to do something with the following:
[...] plugins [need to] be specified in a way that Gradle can easily
and quickly extract [them], before executing the rest of the build
script. It also requires that the definition of plugins to use be
somewhat static.
But rejoice:
Future versions of Gradle will remove this restriction.
I also faced similar issue recently and it got solved by changing Gradle settings in Intellij as follows:

Resources