开发Gradle plugin
最近在学习如何编写 Gradle plugin
, 记录一下学习心得
插件分类
Gradle
插件有三种实现方式 Script plugins
、Precomplied script plugins
和 Binary plugins
Script plugins
: 在build.gradle
中直接实现插件逻辑,只能在当前构建中使用。Precomplied script plugins
: 在项目中的独立文件(.gradle
或.gradle.kts
)中实现插件逻辑,可以在项目中的多个构建中使用。Binary plugins
: 使用独立项目实现插件逻辑,并打包为jar文件,在项目中通过引用jar文件来使用。
本文使用独立项目来制作 Binary plugins
.
环境准备
需要安装 Java
和 Gradle
文中使用的是 Java 21.0.3
和 Gradle 8.10.2
初始化项目
gradle init --type java-gradle-plugin
Welcome to Gradle 8.10.2!
Here are the highlights of this release:
- Support for Java 23
- Faster configuration cache
- Better configuration cache reports
For more details see https://docs.gradle.org/8.10.2/release-notes.html
Starting a Gradle Daemon (subsequent builds will be faster)
Project name (default: gradle-plugin-demo):
Select build script DSL:
1: Kotlin
2: Groovy
Enter selection (default: Kotlin) [1..2]
Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]
> Task :init
For more information, please refer to https://docs.gradle.org/8.10.2/userguide/custom_plugins.html in the Gradle documentation.
BUILD SUCCESSFUL in 1m 5s
1 actionable task: 1 executed
运行 init
命令之后会需要回答3个问题
Project name
: 设置项目名字,(默认是当前目录的名字)Build script DSL
:选择构建脚本的语言,默认是Kotlin
New APIs and behavior
: 是否启用新的API和特性,默认是no
gradle的项目都有一个构建文件 build.gradle
根据构建语言的不同文件的后缀名有所区别
Groovy
的是 build.gradle
, Kotlin
的是 build.gradle.kts
初始化之后的目录结构如下:
.
├── .gitattributes
├── .gitignore
├── gradle
│ ├── libs.versions.toml
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── plugin
│ ├── build.gradle.kts
│ └── src
│ ├── functionalTest
│ │ └── java
│ │ └── org
│ │ └── example
│ │ └── GradlePluginDemoPluginFunctionalTest.java
│ ├── main
│ │ ├── java
│ │ │ └── org
│ │ │ └── example
│ │ │ └── GradlePluginDemoPlugin.java
│ │ └── resources
│ └── test
│ ├── java
│ │ └── org
│ │ └── example
│ │ └── GradlePluginDemoPluginTest.java
│ └── resources
└── settings.gradle.kts
19 directories, 12 files
grale
: 配置gradle-wrapper用来定义项目使用的gradle的版本,libs.versions.toml
是gradle
的依赖版本管理文件是 [[Gradle版本目录(Version Catalogs)]] 的另一种实现方式plugin
: 插件代码的目录,包含核心代码,测试代码settings.gradle.kts
:Gradle
的配置文件
Plugin id
我们可以在 build.gradle.kts
文件中看到如下内容
gradlePlugin {
// Define the plugin
val greeting by plugins.creating {
id = "org.example.greeting"
implementationClass = "org.example.GradlePluginDemoPlugin"
}
}
这里定义我们的插件的 id
, 这个 id
就是插件的唯一标识。
入口类
public class GradlePluginDemoPlugin implements Plugin<Project> {
public void apply(Project project) {
// Register a task
project.getTasks().register("greeting", task -> {
task.doLast(s -> System.out.println("Hello from plugin 'org.example.greeting'"));
});
}
}
实现 Plugin<Project>
的类将作为插件的入口类 , 在一个插件在 Gradle
中被使用将会执行入口类的 apply(Project)
方法。
project.getTasks().register("greeting", task -> {
task.doLast(s -> System.out.println("Hello from plugin 'org.example.greeting'"));
});
通过 register
我们可以注册一个名字叫做 greeting
的 task
到 Gradle
的构建中. 这个task在运行的时候会在控制台输出 Hello from plugin 'org.example.greeting'
现在我们可以运行一下 org.example.GradlePluginDemoPluginFunctionalTest#canRunTask()
这个测试方法来看看一下实际效果
> Task :greeting
Hello from plugin 'org.example.greeting'
可以看到名字叫做 greeting
的插件被执行了,然后输出了 Hello form plugin 'org.example.greeting'
这就是我们通过 init
命令创建的 Gradle plugin
的 Hello world
Extension
当前我们 的插件不能配置,只能输出固定的内容。Extension
可以让插件变的可配置。
Extension
允许插件向 project
对象添加自定义的配置块,从而在构建脚本中定义额外的配置选项或行为。
现在让我们给插件定义一个 Extension
package org.example;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Property;
import javax.inject.Inject;
/**
* @author Too_young
*/
public class DemoExtension {
private final Property<String> greeting;
@Inject
public DemoExtension(ObjectFactory objects) {
this.greeting = objects.property(String.class).convention("Default greeting");
}
public Property<String> getGreeting() {
return greeting;
}
}
这是一个简单的 Extension
类,它只有一个属性 greeting
,我们在初始化的时候给它设置了一个默认值 Default greeting
.
[!tip]
Property<String>
: 表示一个延迟计算的值ObjectFactory
: 用以创建Gradle
的各种类型的对象
延迟计算
是Gradle
的一个特性,它的作用就是在Property
对象中的值在被调用的时候才会计算Property
中的值。
我们有了一个 Extension
, 现在我们需要将 Extension
加入到 Gradle
中,这样我们就能在构建文件(build.gradle)中使用它
/*
* This source file was generated by the Gradle 'init' task
*/
package org.example;
import org.gradle.api.Project;
import org.gradle.api.Plugin;
/**
* A simple 'hello world' plugin.
*/
public class GradlePluginDemoPlugin implements Plugin<Project> {
public void apply(Project project) {
// add Extension
var extension = project.getExtensions().create("demo", DemoExtension.class);
var greeting = extension.getGreeting();
// Register a task
project.getTasks().register("greeting", task -> {
task.doLast(s -> System.out.println(greeting.get()));
});
}
}
在入口类中我们通过 project.getExtensions().create("demo", DemoExtension.class)
向 Gradle
中添加了一个名字为 demo
的 Extension
。
关于延迟计算, 当代码运行到 greeting.get()
的时候,Gradle
才会开始计算 greeting
的值
现在我们的插件一个有了一个 Extension
并且已经加入到了 Gradle
中它的名字是 demo
,我们来看已一下要如何使用这个 Extension
.
plugins {
id("org.example.greeting")
}
demo {
greeting = "greeting from build.gradle.kts"
}