Gradle是一个专注于构建自动化和支持多语言开发的构建工具。使用Gradle构建工具,我们可以方便又快速地实现自己的构建目标。Gradle所有有用的特性都是由插件提供的。一个Gradle插件打包了可重用的构建逻辑,可以在不同的项目和构建中使用。Gradle允许你实现自己的自定义插件,这样你就可以重用你的构建逻辑,并与其他人分享。
你可以使用任何你喜欢的语言来实现一个自定义插件,只要这个实现以字节码的形式编译即可。对于本文,我们将使用Groovy作为实现语言。如果你愿意,可以使用Java或者Scala。一般来说,使用Groovy是最简单的选择,因为Gradle API被设计用来与Groovy一起很好的协作。具体Groovy语言的基础知识可以阅读 Gradle构建之Groovy语言基础 这篇文章。
在学习自定义Gradle插件之前,你需要了解一些Gradle构建的基础知识,比如:下载安装、执行任务、编写构建脚本等等。具体Gradle构建的基础知识可以阅读 Gradle构建之Gradle构建基础 这篇文章。
要自定义一个Gradle插件,你需要写一个实现了 Plugin 接口的类。当你在构建脚本中应用该插件时,Gradle会实例化这个插件类,并调用插件类实例的 Plugin.apply(T) 方法。项目对象将作为参数被传递进去,插件类实例将使用它来配置项目。
Gradle支持两种类型的任务。一种是 简单任务。你可以用一个配置闭包来定义简单任务,配置闭包决定了任务的行为。这种任务适用于在构建脚本中实现一次性的任务。另一种是 增强任务。这种任务将行为定义在任务中,并提供一些属性。你可以使用这些属性来配置任务的行为。增强任务可以让你在不同的地方重用一个行为。
通常,Gradle插件使用增强任务。要自定义一个增强任务,你需要写一个继承自 DefaultTask 的类。在这个类中定义任务的行为和属性。任务的行为是通过在任务类中添加一个方法并标记 TaskAction 注解来定义的。当任务执行时,Gradle将调用该方法。
自定义Gradle插件的方式有三种:
自定义Gradle插件的三种方式各有优缺点。本文将主要介绍采用独立项目来自定义Gradle插件。
这里采用构建脚本的方式来自定义Hello World插件。新建一个build.gradle文件,输入如下代码:
apply plugin: HelloWorldPlugin
class HelloWorldPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.task('hello') {
doLast {
println 'Hello World!'
}
}
}
}
在当前目录下,输入”gradle hello”命令执行构建。如下所示:
$ gradle hello
:hello
Hello World!
BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
可以看到,输出了”Hello World!”字符串,成功地自定义了插件。
接下来我们采用独立项目来自定义一个简单的Gradle插件。项目源码地址:https://github.com/chongyucaiyan/GradlePluginDemo
新建一个IntelliJ IDEA项目。采用Gradle来构建项目。如下图所示:
点击Next,配置插件信息。如下图所示:
点击Next,配置项目。如下图所示:
点击Next,配置项目名和模块名。如下图所示:
点击Finish。稍等片刻,我们就可以看到IntelliJ IDEA生成的项目。如下图所示:
Gradle Wrapper是你应该添加到项目中的内容。通过Wrapper,任何人无需预先安装Gradle就可以构建你的项目。同时,这样确保了构建用户使用的Gradle版本与项目发布的Gradle版本一致。
在项目的根目录下,输入 “gradle wrapper” 命令执行wrapper任务。如下所示:
$ gradle wrapper
:wrapper
BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed
执行wrapper任务之后,你可以在项目的根目录下找到以下文件:
生成Gradle Wrapper之后,我们就可以直接使用 gradlew 命令来构建项目了。gradlew命令与gradle命令的使用方法是一致的。
修改greetingplugin目录下的build.gradle构建脚本,主要是应用 groovy 插件,并添加 Gradle API 作为编译时依赖。如下所示:
apply plugin: 'groovy'
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile gradleApi()
compile localGroovy()
}
group 'com.github.cyc'
version '0.0.1'
在 greetingplugin/src/main/groovy/ 目录下,逐级创建com.github.cyc包路径。在cyc包下,新建一个名为GreetingTask的Groovy类。输入如下代码:
class GreetingTask extends DefaultTask {
String greeting = 'hello from GreetingTask'
@TaskAction
def greet() {
println greeting
}
}
这里定义了一个名为GreetingTask的任务,并定义了任务的行为和一个名为greeting的属性。
在cyc包下,新建一个名为GreetingPlugin的Groovy类。输入如下代码:
class GreetingPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
// 创建 greetingExtension 扩展对象
project.extensions.create('greetingExtension', GreetingPluginExtension)
// 创建 hello 任务
project.task('hello', type: GreetingTask) {
greeting = 'hello from GreetingPlugin'
doLast {
println "${project.greetingExtension.message} from ${project.greetingExtension.greeter}"
}
}
}
}
class GreetingPluginExtension {
String message
String greeter
}
这里创建了一个名为greetingExtension的扩展对象和一个类型为GreetingTask名为hello的任务。
通常,Gradle插件需要从构建脚本中获得一些配置。有一种方法是使用 扩展对象。Gradle的Project对象有一个关联的ExtensionContainer对象,它可以跟踪传递给插件的所有设置和属性。为了捕获输入,只需要将一个符合Java Bean规则的类添加到ExtensionContainer对象的扩展列表中。
当我们在构建脚本中应用一个插件时,Gradle将在插件JAR包的 META-INF/gradle-plugins/ 目录下寻找一个文件名与插件ID相匹配的属性文件来获取具体的插件实现。
在 greetingplugin/src/main/resources/META-INF/gradle-plugins/ 目录下,新建一个com.github.cyc.greeting.properties属性文件。输入如下内容:
implementation-class=com.github.cyc.GreetingPlugin
注意:属性文件名要与插件ID相匹配,implementation-class属性要指定具体的插件实现类。
通常,插件ID的命名方式与Java包名的命名方式类似。这有助于避免冲突,并提供了一种方法来对具有类似所有权的插件进行分组。插件ID应该是名称空间和插件名称的组合,例如:com.github.cyc.greeting。
我们可以把插件发布到本地供自己使用,也可以把插件发布到远程仓库供任何人使用。
修改greetingplugin目录下的build.gradle构建脚本,主要是应用 maven 插件,并配置发布插件的 uploadArchives 任务。如下所示:
apply plugin: 'groovy'
apply plugin: 'maven'
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile gradleApi()
compile localGroovy()
}
group 'com.github.cyc'
version '0.0.1'
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('../repo'))
}
}
}
在项目的根目录下,输入 “./gradlew uploadArchives” 命令发布插件。如下所示:
$ ./gradlew uploadArchives
:greetingplugin:compileJava NO-SOURCE
:greetingplugin:compileGroovy
:greetingplugin:processResources
:greetingplugin:classes
:greetingplugin:jar
:greetingplugin:uploadArchivesCould not find metadata com.github.cyc:greetingplugin/maven-metadata.xml in remote (file:/E:/Gradle/Learning/GradlePluginDemo/repo)
BUILD SUCCESSFUL in 5s
4 actionable tasks: 4 executed
执行uploadArchives任务之后,你可以在项目根目录下的repo目录下找到发布的插件。
常见的Maven远程仓库有 Maven Central 和 jCenter。发布开源库到这两个远程仓库的过程比较繁琐冗长。JitPack 可以简单快速地发布开源库,所以我们将发布插件到JitPack上。
首先,使用Gradle构建的项目需要应用 maven 或者 maven-publishing 插件。这里我们使用maven插件,那么需要在构建脚本中添加如下代码:
apply plugin: 'maven'
group = 'com.github.YourUsername'
在本文的前一小节 发布到本地 中,我们的构建脚本已经包含了上面的代码,所以我们不需要修改构建脚本。虽然group属性的值不是用的Github上的用户名,但是不影响发布。JitPack在发布插件时还是会使用Github上的用户名。刚好我们可以以此来区分发布到本地和远程仓库的插件。
然后,将项目代码上传到 Github 上。具体如何上传代码到Github上,这里就不介绍了。注意:最终JitPack发布的插件的ArtifactId是Github的仓库名。
接着,给上传到Github上的项目打一个 Tag 或者创建一个 Release。具体如何给Github上的项目打一个Tag或者创建一个Release,这里就不介绍了。
最后,复制Github上的项目仓库地址到 JitPack官网。如下图所示:
点击 Look up 按钮。稍等片刻,JitPack就为你生成了插件库。如下图所示:
点击 Get it 按钮。JitPack将为你生成应用插件的代码。如下图所示:
发布成功之后,我们就可以在构建脚本中应用该插件了。
要在构建脚本中使用非Gradle内置的插件,你需要将插件类添加到构建脚本的类路径中。要做到这一点,你需要使用一个 buildscript {} 块。在buildscript {} 块中,使用 repositories 指定插件的仓库地址,使用 dependencies 指定插件的类路径。
在项目根目录下,新建一个demolocal目录。在demolocal目录下,新建一个build.gradle文件。输入如下代码:
buildscript {
repositories {
maven {
url uri('../repo')
}
}
dependencies {
classpath 'com.github.cyc:greetingplugin:0.0.1'
}
}
apply plugin: 'com.github.cyc.greeting'
// 配置扩展对象
greetingExtension {
message = 'Hi'
greeter = 'demolocal'
}
构建脚本应用了我们自定义的插件,并使用配置闭包配置了插件的扩展对象。
在demolocal目录下,输入”../gradlew hello”命令执行构建。如下所示:
$ ../gradlew hello
:hello
hello from GreetingPlugin
Hi from demolocal
BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
可以看到,我们成功地应用了本地插件。
在项目根目录下,新建一个demoremote目录。在demoremote目录下,新建一个build.gradle文件。输入如下代码:
buildscript {
repositories {
maven {
url 'https://jitpack.io'
}
}
dependencies {
classpath 'com.github.chongyucaiyan:GradlePluginDemo:0.0.1'
}
}
apply plugin: 'com.github.cyc.greeting'
// 配置扩展对象
greetingExtension {
message = 'Hi'
greeter = 'demoremote'
}
构建脚本应用了我们自定义的插件,并使用配置闭包配置了插件的扩展对象。
在demoremote目录下,输入”../gradlew hello”命令执行构建。如下所示:
$ ../gradlew hello
:hello
hello from GreetingPlugin
Hi from demoremote
BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed
可以看到,我们成功地应用了远程仓库插件。
Gradle插件打包了可重用的构建逻辑,可以在不同的项目和构建中使用。Gradle允许你实现自己的自定义插件,这样你就可以重用你的构建逻辑,并与其他人分享。