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

此外,如果您发现自己在子项目之间重复构建逻辑,并且需要一种更好的方式来组织它,约定插件可以提供帮助。

脚本插件

插件是任何实现 Plugin 接口的类。例如,这是一个 “hello world” 插件

build.gradle.kts
abstract class SamplePlugin : Plugin<Project> { (1)
    override fun apply(project: Project) {  (2)
        project.tasks.register("ScriptPlugin") {
            doLast {
                println("Hello world from the build file!")
            }
        }
    }
}

apply<SamplePlugin>() (3)
build.gradle
class SamplePlugin implements Plugin<Project> { (1)
    void apply(Project project) {   (2)
        project.tasks.register("ScriptPlugin") {
            doLast {
                println("Hello world from the build file!")
            }
        }
    }
}

apply plugin: SamplePlugin (3)
1 扩展 org.gradle.api.Plugin 接口。
2 覆盖 apply 方法。
3 apply 插件到项目。

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

创建一个类,它扩展了 Plugin 接口

build.gradle.kts
abstract class SamplePlugin : Plugin<Project> {
}
build.gradle
class SamplePlugin implements Plugin<Project> {
}

2. 覆盖 apply 方法

apply() 方法中添加任务和其他逻辑

build.gradle.kts
override fun apply() {

}
build.gradle
void apply(Project project) {

}

3. apply 插件到您的项目

当在您的项目中应用 SamplePlugin 时,Gradle 会调用定义的 fun apply() {} 方法。这会将 ScriptPlugin 任务添加到您的项目中

build.gradle.kts
apply<SamplePlugin>()
build.gradle
apply plugin: SamplePlugin

请注意,这是一个简单的 hello-world 示例,并反映最佳实践。

建议使用脚本插件。

开发插件的最佳实践是创建约定插件二进制插件

预编译脚本插件

预编译脚本插件提供了一种快速原型设计和实验的简便方法。它们允许您使用 Groovy 或 Kotlin DSL 将构建逻辑打包为 *.gradle(.kts) 脚本文件。这些脚本位于特定的目录中,例如 src/main/groovysrc/main/kotlin

要应用一个插件,只需使用从脚本文件名(不带 .gradle)派生的 ID。您可以将文件本身视为插件,因此您不需要在预编译脚本中子类化 Plugin 接口。

让我们看一个具有以下结构的示例

.
└── buildSrc
    ├── build.gradle.kts
    └── src
       └── main
          └── kotlin
             └── my-create-file-plugin.gradle.kts

我们的 my-create-file-plugin.gradle.kts 文件包含以下代码

buildSrc/src/main/kotlin/my-create-file-plugin.gradle.kts
abstract class CreateFileTask : DefaultTask() {
    @get:Input
    abstract val fileText: Property<String>

    @Input
    val fileName = "myfile.txt"

    @OutputFile
    val myFile: File = File(fileName)

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

tasks.register<CreateFileTask>("createMyFileTaskInConventionPlugin") {
    group = "from my convention plugin"
    description = "Create myfile.txt in the current directory"
    fileText.set("HELLO FROM MY CONVENTION PLUGIN")
}
buildSrc/src/main/groovy/my-create-file-plugin.gradle
abstract class CreateFileTask extends DefaultTask {
    @Input
    abstract Property<String> getFileText()

    @Input
    String fileName = "myfile.txt"

    @OutputFile
    File getMyFile() {
        return new File(fileName)
    }

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

tasks.register("createMyFileTaskInConventionPlugin", CreateFileTask) {
    group = "from my convention plugin"
    description = "Create myfile.txt in the current directory"
    fileText.set("HELLO FROM MY CONVENTION PLUGIN")
}

现在可以在任何子项目的 build.gradle(.kts) 文件中应用预编译脚本

build.gradle.kts
plugins {
    id("my-create-file-plugin")  // Apply the pre-compiled convention plugin
    `kotlin-dsl`
}
build.gradle
plugins {
    id 'my-create-file-plugin' // Apply the pre-compiled convention plugin
    id 'groovy' // Apply the Groovy DSL plugin
}

来自插件的 createFileTask 任务现在在您的子项目中可用。

二进制插件

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

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

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

  1. 使用 组合构建

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

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

让我们采用第二种解决方案。此插件已在 Kotlin 中重写,名为 MyCreateFileBinaryPlugin.kt。它仍然存储在 buildSrc

buildSrc/src/main/kotlin/MyCreateFileBinaryPlugin.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 filePath = project.layout.settingsDirectory.file("myfile.txt").asFile.path

    @OutputFile
    val myFile: File = File(filePath)

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

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

可以使用 gradlePlugin{} 代码块发布插件并为其指定 id,以便可以在根目录中引用它

buildSrc/build.gradle.kts
group = "com.example"
version = "1.0.0"

gradlePlugin {
    plugins {
        create("my-binary-plugin") {
            id = "com.example.my-binary-plugin"
            implementationClass = "MyCreateFileBinaryPlugin"
        }
    }
}

publishing {
    repositories {
        mavenLocal()
    }
}
buildSrc/build.gradle
group = 'com.example'
version = '1.0.0'

gradlePlugin {
    plugins {
        create("my-binary-plugin") {
            id = "com.example.my-binary-plugin"
            implementationClass = "MyCreateFileBinaryPlugin"
        }
    }
}

publishing {
    repositories {
        mavenLocal()
    }
}

然后,可以在构建文件中应用插件

build.gradle.kts
plugins {
    id("my-create-file-plugin")  // Apply the pre-compiled convention plugin
    id("com.example.my-binary-plugin") // Apply the binary plugin
    `kotlin-dsl`
}
build.gradle
plugins {
    id 'my-create-file-plugin' // Apply the pre-compiled convention plugin
    id 'com.example.my-binary-plugin' // Apply the binary plugin
    id 'groovy' // Apply the Groovy DSL plugin
}

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