I'm creating a build process with Gradle and I want to provide a plugin that uses Java code. The Gradle plugin documentation says this is possible:
You can implement a custom plugin in any language you like, provided the implementation ends up compiled as bytecode. For the examples here, we are going to use Groovy as the implementation language. You could use Java or Scala instead, if you want.
However, after multiple hours of Googling and reading, I have yet to find any explanation of how to create a Gradle custom plugin with Java. It seems like you could create the code for it in a directory like:
<rootProjectDir>/buildSrc/src/main/java/
MyGradlePlugin.java
MyGradleTasks.java
But the question then becomes:
- How to implement the plugin class and tasks in Java to be compatible with Gradle?
How to get Gradle to recognize the Java classes and tasks so you can use them in a build?
Can you just reference the plugin class like you do for the Groovy equivalent?
src/main/resources/META-INF/gradle-plugins/myjavaplugin.properties
implementation-class=org.me.MyGradlePlugin
I realize that I could just call the Java code with project.javaexec
or JavaExec
, but I'm concerned this will make my build process less portable.
Here's a basic, stand-alone, Java-based Gradle plugin and the steps to get it working:
- Make sure Gradle 1.6 or higher and Java JDK is installed
- Create these files below with the directory structure indicated
- Change directories to the
<projectRoot>/plugin
directory
- Execute the plugin build:
$ gradle uploadArchives
This (very important) step compiles the Java code and puts it in your local Maven repo (../repo).
- Now execute the consumer script by changing directories to
<projectRoot>/consumer
- Execute the script that depends on the plugin:
$ gradle checkitout
Java Classes
projectRoot/plugin/src/main/java/org/joefernandez/gradle/MyJavaPlugin.java
package org.joefernandez.gradle;
import org.gradle.api.Project;
import org.gradle.api.Plugin;
public class MyJavaPlugin implements Plugin<Project> {
@Override
public void apply(Project target) {
target.task("javaTask");
}
}
projectRoot/plugin/src/main/java/org/joefernandez/gradle/MyJavaTask.java
package org.joefernandez.gradle;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;
public class MyJavaTask extends DefaultTask {
@TaskAction
public void javaTask() {
System.out.println("Hello from MyJavaTask");
}
}
Plugin Class declaration
projectRoot/plugin/src/main/resources/META-INF/gradle-plugins/test-plugin.properties
implementation-class=org.joefernandez.gradle.MyJavaPlugin
Plugin Build Script
Note the uploadArchives task: You must run this task to make the plugin available to the consumer script.
projectRoot/plugin/build.gradle
apply plugin: 'java'
dependencies {
compile gradleApi()
}
apply plugin: 'maven'
repositories {
mavenCentral()
}
dependencies {
testCompile 'junit:junit:4.11'
}
group = 'org.joefernandez'
version = '1.0-SNAPSHOT'
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('../repo'))
}
}
}
Settings for the Plugin
projectRoot/plugin/settings.gradle
rootProject.name = 'MyJavaPlugin'
Root script
projectRoot/build.gradle
apply plugin: 'java'
dependencies {
compile gradleApi()
}
Consumer script
projectRoot/consumer/build.gradle
buildscript {
repositories {
maven {
url uri('../repo')
}
}
dependencies {
classpath group: 'org.joefernandez',
name: 'MyJavaPlugin',
version: '1.0-SNAPSHOT'
}
}
apply plugin: 'test-plugin'
task checkitout(type: org.joefernandez.gradle.MyJavaTask) {
println("running consumer task!")
}
I would like to propose a few amendments to Joe's great answer.
projectRoot/plugin/src/main/java/org/joefernandez/gradle/MyJavaPlugin.java
This change wires the task into the supplied project without the need to sub-class it.
package org.joefernandez.gradle;
import org.gradle.api.Project;
import org.gradle.api.Plugin;
public class MyJavaPlugin implements Plugin<Project> {
@Override
public void apply(Project target) {
// CHANGE HERE
target.getTasks().create("javaTask", MyJavaTask.class);
}
}
Consumer script
projectRoot/consumer/build.gradle
This change removes the now unnecessary task definition
buildscript {
repositories {
maven {
url uri('../repo')
}
}
dependencies {
classpath group: 'org.joefernandez',
name: 'MyJavaPlugin',
version: '1.0-SNAPSHOT'
}
}
apply plugin: 'test-plugin'
// CHANGE HERE
This them changes step 6, to:
Execute the script that depends on the plugin: $ gradle javaTask
Plugin classes must implement the org.gradle.api.Plugin
interface. Task classes typically extend the org.gradle.api.DefaultTask
class. It doesn't matter which (JVM) language is used for this. The Gradle codebase contains many plugin and task classes implemented in Java.
How to build and ship a plugin is another question, again mostly unrelated to which language is used. See the Gradle User Guide and samples/customPlugin
in the full Gradle distribution.