Gradle 的大部分功能都是通过插件提供的,包括随 Gradle 分发的核心插件、第三方插件以及构建中定义的脚本插件。

插件引入新任务(例如,JavaCompile)、域对象(例如,SourceSet)、约定(例如,将 Java 源定位于src/main/java),并扩展核心或其他插件对象。

Gradle 中的插件对于自动化常见构建任务、与外部工具或服务集成以及定制构建过程以满足特定项目需求至关重要。它们还充当组织构建逻辑的主要机制。

插件的好处

在构建脚本中编写许多任务并复制配置块可能会变得混乱。与直接向构建脚本添加逻辑相比,插件具有以下几个优点:

  • 促进可重用性:减少跨项目重复类似逻辑的需要。

  • 增强模块化:允许更加模块化和有组织的构建脚本。

  • 封装逻辑:保持命令式逻辑分离,从而实现更具声明性的构建脚本。

插件分发

您可以利用 Gradle 和 Gradle 社区的插件,也可以创建自己的插件。

插件可以通过三种方式使用:

  1. 核心插件- Gradle 开发并维护一组核心插件

  2. 社区插件- 在远程存储库(例如 Maven 或Gradle 插件门户)中共享的 Gradle 插件。

  3. 本地插件- Gradle 使用户能够使用API创建自定义插件。

插件类型

插件可以实现为二进制插件预编译脚本插件脚本插件

二进制插件

二进制插件是通常用 Java 或 Kotlin DSL 编写的编译插件,打包为 JAR 文件。它们被应用到使用块的项目中plugins {}。与脚本插件或预编译脚本插件相比,它们提供更好的性能和可维护性。

预编译脚本插件

预编译脚本插件是 Groovy DSL 或 Kotlin DSL 脚本,它们被编译并作为打包在库中的 Java 类文件分发。它们被应用到使用块的项目中plugins {}。它们提供了一种跨项目重用复杂逻辑的方法,并允许更好地组织构建逻辑。

脚本插件

脚本插件是 Groovy DSL 或 Kotlin DSL 脚本,它们使用语法直接应用于 Gradle 构建脚本apply from:。它们在构建脚本中内联应用,以添加功能或自定义构建过程。它们使用起来很简单。

插件通常作为脚本插件启动(因为它们很容易编写)。然后,随着代码变得更有价值,它会被迁移到可以在多个项目或组织之间轻松测试和共享的二进制插件。

使用插件

要使用插件中封装的构建逻辑,Gradle 需要执行两个步骤。首先,它需要解析插件,然后需要将插件应用到目标,通常是Project.

  1. 解析插件意味着找到包含给定插件的 JAR 的正确版本并将其添加到脚本类路径中。一旦插件被解析,它的 API 就可以在构建脚本中使用。脚本插件是自我解析的,因为它们是从应用它们时提供的特定文件路径或 URL 解析的。作为 Gradle 发行版的一部分提供的核心二进制插件会自动解析。

  2. 应用插件意味着在项目上执行插件的Plugin.apply(T) 。

建议使用插件DSL一步解决并应用插件。

解决插件问题

Gradle 提供了核心插件(例如,JavaPluginGroovyPluginMavenPublishPlugin等)作为其分发的一部分,这意味着它们会自动解析。

核心插件使用插件名称应用在构建脚本中:

plugins {
    id «plugin name»
}

例如:

构建.gradle
plugins {
    id("java")
}

非核心插件必须先解析后才能应用。非核心插件由构建文件中的唯一 ID 和版本来标识:

plugins {
    id «plugin id» version «plugin version»
}

并且必须在设置文件中指定插件的位置:

设置.gradle
pluginManagement {
    repositories {
        gradlePluginPortal()
    }
    maven {
        url 'https://maven.example.com/plugins'
    }
}

解决和应用插件还有其他注意事项:

# 使用 例如:

1

核心社区本地插件应用到特定项目。

plugins构建文件中的块

plugins {
  id("org.barfuin.gradle.taskinfo") version "2.1.0"
}

2

将通用核心社区本地插件应用到多个子项目。

buildSrc目录中的构建脚本

plugins {
    id("org.barfuin.gradle.taskinfo") version "2.1.0"
}
repositories {
    jcenter()
}
dependencies {
    implementation(Libs.Kotlin.coroutines)
}

3

应用构建脚本本身所需的核心社区本地插件。

buildscript构建文件中的块

buildscript {
  repositories {
    maven {
      url = uri("https://plugins.gradle.org/m2/")
    }
  }
  dependencies {
    classpath("org.barfuin.gradle.taskinfo:gradle-taskinfo:2.1.0")
  }
}
plugins {
  id("org.barfuin.gradle.taskinfo") version "2.1.0"
}

4

应用本地脚本插件。

apply()构建文件中的遗留方法

apply(plugin = "org.barfuin.gradle.taskinfo")
apply<MyPlugin>()

plugins{}1. 使用块应用插件

插件 DSL 提供了一种简洁便捷的方式来声明插件依赖项。

插件块配置一个实例PluginDependenciesSpec

plugins {
    application                                     // by name
    java                                            // by name
    id("java")                                      // by id - recommended
    id("org.jetbrains.kotlin.jvm") version "1.9.0"  // by id - recommended
}

核心 Gradle 插件的独特之处在于它们提供短名称,例如java核心JavaPlugin

要应用核心插件,可以使用短名称:

build.gradle.kts
plugins {
    java
}
build.gradle
plugins {
    id 'java'
}

所有其他二进制插件必须使用插件 ID 的完全限定形式(例如com.github.foo.bar)。

要从Gradle 插件门户应用社区插件,必须使用完全限定的插件 id (全局唯一标识符):

build.gradle.kts
plugins {
    id("com.jfrog.bintray") version "1.8.5"
}
build.gradle
plugins {
    id 'com.jfrog.bintray' version '1.8.5'
}

PluginDependenciesSpec有关使用插件 DSL 的更多信息,请参阅 参考资料。

插件 DSL 的限制

插件 DSL 为用户提供了方便的语法,并且让 Gradle 能够快速确定使用哪些插件。这使得 Gradle 能够:

  • 优化插件类的加载和复用。

  • 为编辑者提供有关构建脚本中潜在属性和值的详细信息。

然而,DSL 要求插件是静态定义的。

plugins {}区块机制和“传统”方法机制之间存在一些关键的区别apply()。还有一些限制和可能的限制。

约束语法

plugins {}块不支持任意代码。

它被限制为幂等(每次产生相同的结果)和无副作用(Gradle 可以随时安全执行)。

形式为:

build.gradle.kts
plugins {
    id(«plugin id»)                             (1)
    id(«plugin id») version «plugin version»    (2)
}
1 for core Gradle plugins or plugins already available to the build script
2 for binary Gradle plugins that need to be resolved
build.gradle
plugins {
    id «plugin id»                            (1)
    id «plugin id» version «plugin version»   (2)
}
1 for core Gradle plugins or plugins already available to the build script
2 for binary Gradle plugins that need to be resolved

其中«plugin id»«plugin version»是一个字符串。

其中«plugin id»«plugin version»必须是常量、文字字符串。

plugins{}块还必须是构建脚本中的顶级语句。它不能嵌套在另一个构造中(例如,if 语句或for 循环)。

仅在构建脚本和设置文件中

该块只能在项目的构建脚本和文件plugins{}中使用。它必须出现在任何其他块之前。它不能在脚本插件或初始化脚本中使用。build.gradle(.kts)settings.gradle(.kts)

将插件应用到所有子项目

假设您有一个多项目构建,您可能希望将插件应用于构建中的部分或全部子项目,但不应用于项目root

虽然该块的默认行为plugins{}是立即resolve 使用 apply插件,但您可以使用apply false语法告诉 Gradle 不要将插件应用到当前项目。然后,plugins{}在子项目的构建脚本中使用没有版本的块:

settings.gradle.kts
include("hello-a")
include("hello-b")
include("goodbye-c")
build.gradle.kts
plugins {
    id("com.example.hello") version "1.0.0" apply false
    id("com.example.goodbye") version "1.0.0" apply false
}
hello-a/build.gradle.kts
plugins {
    id("com.example.hello")
}
hello-b/build.gradle.kts
plugins {
    id("com.example.hello")
}
goodbye-c/build.gradle.kts
plugins {
    id("com.example.goodbye")
}
settings.gradle
include 'hello-a'
include 'hello-b'
include 'goodbye-c'
build.gradle
plugins {
    id 'com.example.hello' version '1.0.0' apply false
    id 'com.example.goodbye' version '1.0.0' apply false
}
hello-a/build.gradle
plugins {
    id 'com.example.hello'
}
hello-b/build.gradle
plugins {
    id 'com.example.hello'
}
goodbye-c/build.gradle
plugins {
    id 'com.example.goodbye'
}

您还可以通过使用自己的约定插件编写构建逻辑来封装外部插件的版本。

buildSrc2. 从目录应用插件

buildSrc是 Gradle 项目根目录中的一个可选目录,其中包含用于构建主项目的构建逻辑(即插件)。您可以应用驻留在项目buildSrc目录中的插件,只要它们具有已定义的 ID。

以下示例显示如何将my.MyPlugin在 中定义的插件实现类绑定buildSrc到 id“my-plugin”:

buildSrc/build.gradle.kts
plugins {
    `java-gradle-plugin`
}

gradlePlugin {
    plugins {
        create("myPlugins") {
            id = "my-plugin"
            implementationClass = "my.MyPlugin"
        }
    }
}
buildSrc/build.gradle
plugins {
    id 'java-gradle-plugin'
}

gradlePlugin {
    plugins {
        myPlugins {
            id = 'my-plugin'
            implementationClass = 'my.MyPlugin'
        }
    }
}

然后可以通过 ID 应用该插件:

build.gradle.kts
plugins {
    id("my-plugin")
}
build.gradle
plugins {
    id 'my-plugin'
}

buildscript{}3. 使用块应用插件

buildscript块用于:

  1. 全局dependenciesrepositories构建项目所需(在子项目中应用)。

  2. 声明哪些插件可在构建脚本中使用(在build.gradle(.kts)文件本身中)。

因此,当您想在构建脚本本身中使用库时,必须使用以下命令将此库添加到脚本类路径上buildScript

import org.apache.commons.codec.binary.Base64

buildscript {
    repositories {  // this is where the plugins are located
        mavenCentral()
        google()
    }
    dependencies { // these are the plugins that can be used in subprojects or in the build file itself
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2' // used in the task below
        classpath 'com.android.tools.build:gradle:4.1.0' // used in subproject
    }
}

tasks.register('encode') {
    doLast {
        def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
        println new String(encodedString)
    }
}

您可以在需要它的子项目中应用全局声明的依赖项:

plugins {
    id 'com.android.application'
}

作为外部 jar 文件发布的二进制插件可以通过将插件添加到构建脚本类路径然后应用该插件来添加到项目中。

可以使用块将外部 jar 添加到构建脚本类路径,如构建脚本的外部依赖项buildscript{}中所述:

build.gradle.kts
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5")
    }
}

apply(plugin = "com.jfrog.bintray")
build.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5'
    }
}

apply plugin: 'com.jfrog.bintray'

4. 使用遗留apply()方法应用脚本插件

脚本插件是一个临时插件,通常在同一构建脚本中编写和应用。它是使用遗留应用程序方法应用的:

class MyPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        println("Plugin ${this.javaClass.simpleName} applied on ${project.name}")
    }
}

apply<MyPlugin>()

other.gradle让我们看一个在与该文件位于同一目录中的文件中编写的插件的基本示例build.gradle

public class Other implements Plugin<Project> {
    @Override
    void apply(Project project) {
        // Does something
    }
}

首先,使用以下命令导入外部文件:

apply from: 'other.gradle'

然后你就可以应用它:

apply plugin: Other

脚本插件会自动解析,并且可以从本地文件系统上的脚本或远程应用:

build.gradle.kts
apply(from = "other.gradle.kts")
build.gradle
apply from: 'other.gradle'

文件系统位置是相对于项目目录的,而远程脚本位置是使用 HTTP URL 指定的。多个脚本插件(任一形式)可以应用于给定目标。

插件管理

pluginManagement{}块用于配置插件解析的存储库,并为构建脚本中应用的插件定义版本约束。

pluginManagement{}块可以在文件中使用settings.gradle(.kts),它必须是文件中的第一个块:

settings.gradle.kts
pluginManagement {
    plugins {
    }
    resolutionStrategy {
    }
    repositories {
    }
}
rootProject.name = "plugin-management"
settings.gradle
pluginManagement {
    plugins {
    }
    resolutionStrategy {
    }
    repositories {
    }
}
rootProject.name = 'plugin-management'
init.gradle.kts
settingsEvaluated {
    pluginManagement {
        plugins {
        }
        resolutionStrategy {
        }
        repositories {
        }
    }
}
init.gradle
settingsEvaluated { settings ->
    settings.pluginManagement {
        plugins {
        }
        resolutionStrategy {
        }
        repositories {
        }
    }
}

自定义插件存储库

默认情况下,plugins{}DSL 解析来自公共Gradle 插件门户的插件。

许多构建作者还希望解析来自私有 Maven 或 Ivy 存储库的插件,因为它们包含专有的实现细节,或者可以更好地控制哪些插件可用于其构建。

要指定自定义插件存储库,请使用repositories{}内部块pluginManagement{}

settings.gradle.kts
pluginManagement {
    repositories {
        maven(url = "./maven-repo")
        gradlePluginPortal()
        ivy(url = "./ivy-repo")
    }
}
settings.gradle
pluginManagement {
    repositories {
        maven {
            url './maven-repo'
        }
        gradlePluginPortal()
        ivy {
            url './ivy-repo'
        }
    }
}

这告诉 Gradle 在解析插件时首先查看 Maven 存储库,../maven-repo如果在 Maven 存储库中找不到插件,则检查 Gradle 插件门户。如果您不想搜索 Gradle 插件门户,请省略该gradlePluginPortal()行。最后,../ivy-repo将检查Ivy 存储库。

插件版本管理

plugins{}内部的块允许pluginManagement{}在单个位置定义构建的所有插件版本。然后可以通过块通过 id 将插件应用到任何构建脚本plugins{}

以这种方式设置插件版本的好处之一是,它pluginManagement.plugins{}不具有与构建脚本块相同的约束语法plugins{}。这允许从 获取插件版本gradle.properties,或通过其他机制加载插件版本。

通过以下方式管理插件版本pluginManagement

settings.gradle.kts
pluginManagement {
  val helloPluginVersion: String by settings
  plugins {
    id("com.example.hello") version "${helloPluginVersion}"
  }
}
build.gradle.kts
plugins {
    id("com.example.hello")
}
gradle.properties
helloPluginVersion=1.0.0
settings.gradle
pluginManagement {
  plugins {
        id 'com.example.hello' version "${helloPluginVersion}"
    }
}
build.gradle
plugins {
    id 'com.example.hello'
}
gradle.properties
helloPluginVersion=1.0.0

插件版本从设置脚本加载gradle.properties并在设置脚本中配置,允许将插件添加到任何项目而无需指定版本。

插件解析规则

插件解析规则允许您修改plugins{}块中发出的插件请求,例如,更改请求的版本或显式指定实现工件坐标。

要添加解析规则,请使用块resolutionStrategy{}内部pluginManagement{}

settings.gradle.kts
pluginManagement {
    resolutionStrategy {
        eachPlugin {
            if (requested.id.namespace == "com.example") {
                useModule("com.example:sample-plugins:1.0.0")
            }
        }
    }
    repositories {
        maven {
            url = uri("./maven-repo")
        }
        gradlePluginPortal()
        ivy {
            url = uri("./ivy-repo")
        }
    }
}
settings.gradle
pluginManagement {
    resolutionStrategy {
        eachPlugin {
            if (requested.id.namespace == 'com.example') {
                useModule('com.example:sample-plugins:1.0.0')
            }
        }
    }
    repositories {
        maven {
            url './maven-repo'
        }
        gradlePluginPortal()
        ivy {
            url './ivy-repo'
        }
    }
}

这告诉 Gradle 使用指定的插件实现工件,而不是其从插件 ID 到 Maven/Ivy 坐标的内置默认映射。

自定义 Maven 和 Ivy 插件存储库必须包含插件标记工件和实现该插件的工件。请阅读Gradle 插件开发插件,了解有关将插件发布到自定义存储库的更多信息。

有关使用该块的完整文档,请参阅PluginManagementSpecpluginManagement{}

插件标记工件

由于plugins{}DSL 块仅允许通过全局唯一的插件idversion属性来声明插件,因此 Gradle 需要一种方法来查找插件实现工件的坐标。

为此,Gradle 将查找坐标为 的插件标记工件plugin.id:plugin.id.gradle.plugin:plugin.version。该标记需要依赖于实际的插件实现。发布这些标记是由java-gradle-plugin自动完成的。

例如,项目中的以下完整示例sample-plugins展示了如何使用java-gradle-pluginmaven-publish插件和ivy-publish插件的组合将插件发布到 Ivy 和 Maven 存储com.example.hello库。com.example.goodbye

build.gradle.kts
plugins {
    `java-gradle-plugin`
    `maven-publish`
    `ivy-publish`
}

group = "com.example"
version = "1.0.0"

gradlePlugin {
    plugins {
        create("hello") {
            id = "com.example.hello"
            implementationClass = "com.example.hello.HelloPlugin"
        }
        create("goodbye") {
            id = "com.example.goodbye"
            implementationClass = "com.example.goodbye.GoodbyePlugin"
        }
    }
}

publishing {
    repositories {
        maven {
            url = uri(layout.buildDirectory.dir("maven-repo"))
        }
        ivy {
            url = uri(layout.buildDirectory.dir("ivy-repo"))
        }
    }
}
build.gradle
plugins {
    id 'java-gradle-plugin'
    id 'maven-publish'
    id 'ivy-publish'
}

group 'com.example'
version '1.0.0'

gradlePlugin {
    plugins {
        hello {
            id = 'com.example.hello'
            implementationClass = 'com.example.hello.HelloPlugin'
        }
        goodbye {
            id = 'com.example.goodbye'
            implementationClass = 'com.example.goodbye.GoodbyePlugin'
        }
    }
}

publishing {
    repositories {
        maven {
            url layout.buildDirectory.dir("maven-repo")
        }
        ivy {
            url layout.buildDirectory.dir("ivy-repo")
        }
    }
}

在示例目录中运行gradle publish会创建以下 Maven 存储库布局(Ivy 布局类似):

插件标记

旧版插件应用程序

随着插件 DSL的引入,用户应该没有理由使用应用插件的传统方法。此处记录是为了防止构建作者由于当前工作方式的限制而无法使用插件 DSL。

build.gradle.kts
apply(plugin = "java")
build.gradle
apply plugin: 'java'

可以使用插件 id应用插件。在上面的例子中,我们使用短名称“java”来应用JavaPlugin

除了使用插件 ID 之外,还可以通过简单地指定插件的类来应用插件:

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

JavaPlugin上面示例中的符号指的是JavaPlugin。此类并不严格需要导入,因为该org.gradle.api.plugins包会在所有构建脚本中自动导入(请参阅默认导入)。

此外,在 Kotlin 中而不是在 Java 中,需要附加::class后缀来标识类文字.class

此外,在 Groovy 中不需要.class像在 Java 中那样通过追加来标识类文字。

使用版本目录

当项目使用版本目录时,应用时可以通过别名引用插件。

我们来看一个简单的版本目录:

gradle/libs.versions.toml
[versions]
intellij-plugin = "1.6"

[plugins]
jetbrains-intellij = { id = "org.jetbrains.intellij", version.ref = "intellij-plugin" }

然后可以使用以下方法将插件应用于任何构建脚本alias

构建.gradle.kts
plugins {
    alias(libs.plugins.jetbrains.intellij)
}
jetbrains-intellij可用作 Gradle 生成的安全访问器:jetbrains.intellij.

下一步: 学习如何编写插件>>