如果 Gradle 或 Gradle 社区没有提供项目所需的特定功能,那么创建自己的插件可能是一种解决方案。

此外,如果你发现自己在子项目中重复构建逻辑,并且需要一种更好的方法来组织它,那么自定义插件可以提供帮助。

自定义插件

插件是实现 Plugin 接口的任何类。以下示例是最简单的插件,一个“hello world”插件

build.gradle.kts
import org.gradle.api.Plugin
import org.gradle.api.Project

abstract class SamplePlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.tasks.create("SampleTask") {
            println("Hello world!")
        }
    }
}

脚本插件

许多插件都是以构建脚本中编码的脚本插件开始的。这提供了一种在构建插件时快速制作原型和进行试验的简单方法。我们来看一个示例

build.gradle.kts
// Define a task
abstract class CreateFileTask : DefaultTask() {                                     (1)
    @get:Input
    abstract val fileText: Property<String>                                         (2)

    @Input
    val fileName = "myfile.txt"

    @OutputFile
    val myFile: File = File(fileName)

    @TaskAction
    fun action() {
        myFile.createNewFile()
        myFile.writeText(fileText.get())
    }
}

// Define a plugin
abstract class MyPlugin : Plugin<Project> {                                         (3)
    override fun apply(project: Project) {
        tasks {
            register("createFileTask", CreateFileTask::class) {
                group = "from my plugin"
                description = "Create myfile.txt in the current directory"
                fileText.set("HELLO FROM MY PLUGIN")
            }
        }
    }
}

// Apply the local plugin
apply<MyPlugin>()                                                                   (4)
1 子类化 DefaultTask()
2 在任务中使用延迟配置。
3 扩展 org.gradle.api.Plugin 接口。
4 应用脚本插件。

1. 子类化 DefaultTask()

首先,通过子类化 DefaultTask() 构建一个任务。

abstract class CreateFileTask : DefaultTask() { }

此简单任务将一个文件添加到我们应用程序的根目录中。

2. 使用延迟配置

Gradle 有一个称为延迟配置的概念,它允许在实际设置任务输入和输出之前引用它们。这是通过 Property 类类型完成的。

abstract val fileText: Property<String>

此机制的一个优点是,你可以在文件名甚至尚未确定之前,将一个任务的输出文件链接到另一个任务的输入文件。Property 类还知道它链接到哪个任务,从而使 Gradle 能够自动添加所需的依赖任务。

3. 扩展 org.gradle.api.Plugin 接口

接下来,创建一个新类,它扩展 org.gradle.api.Plugin 接口。

abstract class MyPlugin : Plugin<Project> {
    override fun apply() {}
}

你可以在 apply() 方法中添加任务和其他逻辑。

4. 应用脚本插件

最后,在构建脚本中应用本地插件。

apply<MyPlugin>()

当在构建脚本中应用 MyPlugin 时,Gradle 会调用自定义 MyPlugin 类中定义的 fun apply() {} 方法。

这使得该插件可用于应用程序。

不推荐使用脚本插件。脚本插件提供了一种快速构建构建逻辑的简单方法,然后再将其迁移到更永久的解决方案,例如约定插件二进制插件

约定插件

约定插件是封装和重用 Gradle 中常见构建逻辑的一种方法。它们允许你为项目定义一组约定,然后将这些约定应用于其他项目或模块。

上面的示例已重写为存储在 buildSrc 中的约定插件

buildSrc/src/main/kotlin/MyConventionPlugin.kt
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import java.io.File

abstract class CreateFileTask : DefaultTask() {
    @get:Input
    abstract val fileText: Property<String>

    @Input
    val fileName = project.rootDir.toString() + "/myfile.txt"

    @OutputFile
    val myFile: File = File(fileName)

    @TaskAction
    fun action() {
        myFile.createNewFile()
        myFile.writeText(fileText.get())
    }
}

class MyConventionPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.tasks.register("createFileTask", CreateFileTask::class.java) {
            group = "from my plugin"
            description = "Create myfile.txt in the current directory"
            fileText.set("HELLO FROM MY PLUGIN")
        }
    }
}

可以使用 gradlePlugin{} 块为插件指定 id,以便可以在根目录中引用它

buildSrc/build.gradle.kts
gradlePlugin {
    plugins {
        create("my-convention-plugin") {
            id = "com.gradle.plugin.my-convention-plugin"
            implementationClass = "com.gradle.plugin.MyConventionPlugin"
        }
    }
}

gradlePlugin{} 块定义了项目正在构建的插件。使用新创建的 id,可以相应地在其他构建脚本中应用该插件

build.gradle.kts
plugins {
    application
    id("com.gradle.plugin.my-convention-plugin") // Apply the new plugin
}

二进制插件

二进制插件是用编译语言实现并打包为 JAR 文件的插件。它是作为依赖项解析的,而不是从源代码编译的。

对于大多数用例,约定插件必须不频繁地更新。让每个开发人员作为其开发过程的一部分执行插件构建是浪费的,我们反而可以将它们作为二进制依赖项进行分发。

有两种方法可以将上述示例中的约定插件更新为二进制插件。

  1. 使用 复合构建

    settings.gradle.kts
    includeBuild("my-plugin")
  2. 将插件发布 到存储库

    build.gradle.kts
    plugins {
        id("com.gradle.plugin.myconventionplugin") version "1.0.0"
    }

查阅 开发插件章节 了解更多信息。