Gradle的核心故意为现实世界的自动化提供了很少的东西. 所有有用的功能(如编译Java代码的功能)都由plugins添加. 插件添加新任务(例如JavaCompile ),域对象(例如SourceSet ),约定(例如Java源位于src/main/java ),以及扩展核心对象和其他插件的对象.

在本章中,我们讨论如何使用插件以及围绕插件的术语和概念.

What plugins do

Applying a plugin to a project allows the plugin to extend the project’s capabilities. It can do things such as:

  • 扩展Gradle模型(例如,添加可以配置的新DSL元素)

  • 根据约定配置项目(例如,添加新任务或配置合理的默认值)

  • 应用特定的配置(例如,添加组织存储库或强制执行标准)

通过应用插件,而不是向项目构建脚本添加逻辑,我们可以获得许多好处. 应用插件:

  • 促进重用并减少跨多个项目维护相似逻辑的开销

  • 允许更高程度的模块化,增强可理解性和组织性

  • 封装命令性逻辑,并允许构建脚本尽可能地具有声明性

Types of plugins

Gradle中有两种通用的插件类型,即二进制插件和脚本插件. 通过实现插件接口以编程方式编写二进制插件,或使用Gradle的一种DSL语言以声明方式编写二进制插件. 二进制插件可以驻留在构建脚本中,项目层次结构中或插件罐的外部. 脚本插件是其他构建脚本,可以进一步配置构建,并通常采用声明式方法来操纵构建. 尽管它们可以被外部化并可以从远程位置访问,但它们通常在构建中使用.

插件通常起初是脚本插件(因为它们易于编写),然后,随着代码变得更有价值,它被迁移到可以轻松测试并在多个项目或组织之间共享的二进制插件.

Using plugins

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

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

应用插件意味着在要使用插件增强的项目上实际执行插件的Plugin.apply(T) . 应用插件是幂等的 . 也就是说,您可以安全地多次应用任何插件而不会产生副作用.

使用插件的最常见用例是解析插件并将其应用于当前项目. 由于这是一个常见的用例,因此建议构建作者使用插件DSL一步解决和应用插件.

Binary plugins

您可以通过插件的插件ID来应用插件, 插件ID插件的全局唯一标识符或名称. Core Gradle插件的特殊之处在于它们提供了简短的名称,例如核心JavaPlugin的 'java' . 所有其他二进制插件都必须使用插件ID的完全限定形式(例如com.github.foo.bar ),尽管某些旧式插件可能仍使用简短的非限定形式. 放置插件ID的位置取决于您使用的是插件DSL还是buildscript块.

Locations of binary plugins

插件就是实现了Plugin接口的任何类. Gradle提供了核心插件(例如JavaPlugin )作为其分发的一部分,这意味着它们会自动解决. 但是,非核心二进制插件需要先解决,然后才能应用. 这可以通过多种方式实现:

有关定义自己的插件的更多信息,请参见自定义插件 .

Applying plugins with the plugins DSL

插件DSL提供了一种声明插件依赖关系的简洁方便的方法. 它与Gradle插件门户一起使用,可轻松访问核心插件和社区插件. 插件DSL块配置PluginDependenciesSpec的实例.

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

例子1.应用核心插件
build.gradle
plugins {
    id 'java'
}
build.gradle.kts
plugins {
    java
}

要从门户网站应用社区插件,必须使用完全限定的插件ID:

例子2.应用社区插件
build.gradle
plugins {
    id 'com.jfrog.bintray' version '0.4.1'
}
build.gradle.kts
plugins {
    id("com.jfrog.bintray") version "0.4.1"
}

See PluginDependenciesSpec for more information on using the Plugin DSL.

Limitations of the plugins DSL

这种将插件添加到项目中的方法远不止是更方便的语法. 插件DSL的处理方式使Gradle可以非常早,非常迅速地确定正在使用的插件. 这使Gradle可以做一些聪明的事情,例如:

  • 优化插件类的加载和重用.

  • 允许不同的插件使用不同版本的依赖项.

  • 向编辑者提供有关构建脚本中潜在属性和值的详细信息,以提供编辑帮助.

这要求在执行其余构建脚本之前,以Gradle可以轻松,快速提取的方式指定插件. 它还要求要使用的插件的定义一定是静态的.

plugins {}块机制与"传统" apply()方法机制之间存在一些关键区别. 还有一些约束,其中一些是暂时的限制,而该机制仍在开发中,而某些则是新方法固有的.

Constrained Syntax

plugins {}块不支持任意代码. 它是受约束的,以便具有幂等性(每次都产生相同的结果)并且没有副作用(对于Gradle可以随时执行是安全的).

形式是:

build.gradle
plugins {
    id «plugin id»                                            (1)
    id «plugin id» version «plugin version» [apply «false»]   (2)
}
1 用于核心Gradle插件或构建脚本已可用的插件
2 用于需要解决的二进制Gradle插件
build.gradle.kts
plugins {
    `«plugin id»`                                             (1)
    id(«plugin id»)                                           (2)
    id(«plugin id») version «plugin version» [apply «false»]  (3)
}
1 用于核心Gradle插件
2 用于核心Gradle插件或构建脚本已可用的插件
3 用于需要解决的二进制Gradle插件

«plugin id»«plugin version»必须为常量的情况下,可以使用文字,字符串和带有booleanapply语句来禁用立即应用插件的默认行为(例如,您仅希望在subprojects应用它). 不允许其他声明; 它们的存在将导致编译错误.

其中,在#1情况下, «plugin id»是静态的Kotlin扩展属性,以核心插件ID命名; 在#2#3是字符串的情况下. «plugin version»也是一个字符串. 带有booleanapply语句可用于禁用立即应用插件的默认行为(例如,您只想在subprojects应用它).

如果要使用变量定义插件版本,请参见插件版本管理 .

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

Can only be used in build scripts and settings file

plugins {}块当前只能在项目的构建脚本和settings.gradle文件中使用. 不能在脚本插件或初始化脚本中使用.

Gradle的未来版本将删除此限制.

如果plugins {}块的限制令人望而却步,建议的方法是使用buildscript {}块来应用插件.

Applying plugins to subprojects

如果您具有多项目构建 ,则可能要将插件应用于构建中的某些或所有子项目,而不是应用于rootmaster项目. plugins {}块的默认行为是立即resolve apply插件. 但是,您可以使用apply false语法告诉Gradle不要将插件应用于当前项目,然后在subprojects块中使用subprojects apply plugin: «plugin id» ,或者在subprojects构建脚本中使用plugins {}块:

例子3.仅在某些子项目上应用插件
settings.gradle
include 'helloA'
include 'helloB'
include 'goodbyeC'
build.gradle
plugins {
    id 'com.example.hello' version '1.0.0' apply false
    id 'com.example.goodbye' version '1.0.0' apply false
}

subprojects {
    if (name.startsWith('hello')) {
        apply plugin: 'com.example.hello'
    }
}
goodbyeC/build.gradle
plugins {
    id 'com.example.goodbye'
}
settings.gradle.kts
include("helloA")
include("helloB")
include("goodbyeC")
build.gradle.kts
plugins {
    id("com.example.hello") version "1.0.0" apply false
    id("com.example.goodbye") version "1.0.0" apply false
}

subprojects {
    if (name.startsWith("hello")) {
        apply(plugin = "com.example.hello")
    }
}
goodbyeC/build.gradle.kts
plugins {
    id("com.example.goodbye")
}

如果然后运行gradle hello您将看到只有helloA和helloB子项目应用了hello插件.

例子4. gradle hello输出
> gradle hello
:helloA:hello
:helloB:hello
Hello!
Hello!

BUILD SUCCEEDED

Applying plugins from the buildSrc directory

您可以应用驻留在项目的buildSrc目录中的插件,只要它们具有已定义的ID即可. 以下示例说明如何将my.MyPlugin定义的插件实现类my.MyPlugin与ID" my-plugin" 绑定

例子5.用一个ID定义一个buildSrc插件
buildSrc/build.gradle
plugins {
    id 'java-gradle-plugin'
}

gradlePlugin {
    plugins {
        myPlugins {
            id = 'my-plugin'
            implementationClass = 'my.MyPlugin'
        }
    }
}
buildSrc/build.gradle.kts
plugins {
    `java-gradle-plugin`
}

gradlePlugin {
    plugins {
        create("myPlugins") {
            id = "my-plugin"
            implementationClass = "my.MyPlugin"
        }
    }
}

然后可以按常规方式通过ID应用插件:

例子6.从buildSrc应用一个插件
build.gradle
plugins {
    id 'my-plugin'
}
build.gradle.kts
plugins {
    id("my-plugin")
}

Plugin Management

The pluginManagement {} block may only appear in either the settings.gradle file, where it must be the first block in the file, or in an Initialization Script.

例子7.为每个项目和全局配置pluginManagement
settings.gradle
pluginManagement {
    plugins {
    }
    resolutionStrategy {
    }
    repositories {
    }
}
init.gradle
settingsEvaluated { settings ->
    settings.pluginManagement {
        plugins {
        }
        resolutionStrategy {
        }
        repositories {
        }
    }
}
settings.gradle.kts
pluginManagement {
    plugins {
    }
    resolutionStrategy {
    }
    repositories {
    }
}
init.gradle.kts
settingsEvaluated {
    pluginManagement {
        plugins {
        }
        resolutionStrategy {
        }
        repositories {
        }
    }
}
Custom Plugin Repositories

默认情况下, plugins {} DSL从公共Gradle插件门户解析插件. 许多构建作者还希望从私有Maven或Ivy存储库中解析插件,因为这些插件包含专有的实现细节,或者只是为了更好地控制其构建中可用的插件.

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

示例8.示例:使用来自自定义插件存储库的插件.
settings.gradle
pluginManagement {
    repositories {
        maven {
            url '../maven-repo'
        }
        gradlePluginPortal()
        ivy {
            url '../ivy-repo'
        }
    }
}
settings.gradle.kts
pluginManagement {
    repositories {
        maven(url = "../maven-repo")
        gradlePluginPortal()
        ivy(url = "../ivy-repo")
    }
}

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

Plugin Version Management

pluginManagement {} plugins {}块允许将构建的所有插件版本定义在一个位置. 然后可以通过plugins {}块按ID将插件应用于任何构建脚本.

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

示例9.示例:通过pluginManagement管理插件版本.
settings.gradle
pluginManagement {
  plugins {
        id 'com.example.hello' version "${helloPluginVersion}"
    }
}
build.gradle
gradle.properties
helloPluginVersion=1.0.0
settings.gradle.kts
pluginManagement {
  val helloPluginVersion: String by settings
  plugins {
    id("com.example.hello") version "${helloPluginVersion}"
  }
}
build.gradle.kts
gradle.properties
helloPluginVersion=1.0.0

插件版本从gradle.properties加载并在设置脚本中进行配置,从而允许在不指定版本的情况下将插件添加到任何项目中.

Plugin Resolution Rules

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

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

例子10.插件解析策略
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'
        }
    }
}
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")
        }
    }
}

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

自定义Maven和Ivy插件存储库除了实际实现插件的工件外 ,还必须包含插件标记工件 . 有关将插件发布到自定义存储库的更多信息,请阅读Gradle Plugin Development Plugin .

有关使用pluginManagement {}块的完整文档,请参见PluginManagementSpec .

Plugin Marker Artifacts

由于plugins {} DSL块仅允许通过其全局唯一的插件idversion属性来声明插件,因此Gradle需要一种方法来查找插件实现工件的坐标. 为此,Gradle将查找带有坐标plugin.id:plugin.id.gradle.plugin:plugin.version的插件标记工件. 该标记需要依赖于实际的插件实现. 这些标记的发布由java-gradle-plugin自动执行.

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

例子11.完整的插件发布样本
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 '../../consuming/maven-repo'
        }
        ivy {
            url '../../consuming/ivy-repo'
        }
    }
}
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("../../consuming/maven-repo")
        }
        ivy {
            url = uri("../../consuming/ivy-repo")
        }
    }
}

在示例目录中运行gradle publish会导致以下回购布局存在:

pluginMarkers

Legacy Plugin Application

随着插件DSL的引入,用户几乎没有理由使用应用插件的旧方法. 如果构建作者由于当前工作方式的限制而无法使用插件DSL,则在此进行记录.

Applying Binary Plugins

例子12.应用一个二进制插件
build.gradle
apply plugin: 'java'
build.gradle.kts
apply(plugin = "java")

可以使用插件ID来应用插件 . 在上述情况下,我们使用简称' java '来应用JavaPlugin .

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

例子13.按类型应用二进制插件
build.gradle
apply plugin: JavaPlugin
build.gradle.kts
apply<JavaPlugin>()

上面示例中的JavaPlugin符号是指JavaPlugin . 绝对不需要导入org.gradle.api.plugins因为org.gradle.api.plugins包会自动在所有构建脚本中导入(请参见Default imports ).

此外,与在Java中一样,无需在Groovy中附加.class来标识类文字.

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

Applying plugins with the buildscript block

通过将插件添加到构建脚本类路径中,然后应用该插件,可以将已发布为外部jar文件的二进制插件添加到项目中. 可以使用buildscript {}块将外部jar添加到构建脚本类路径中,如构建脚本的外部依赖项中所述 .

例子14.使用带有buildscript块的插件
build.gradle
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:0.4.1'
    }
}

apply plugin: 'com.jfrog.bintray'
build.gradle.kts
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath("com.jfrog.bintray.gradle:gradle-bintray-plugin:0.4.1")
    }
}

apply(plugin = "com.jfrog.bintray")

Script plugins

例子15.应用脚本插件
build.gradle
apply from: 'other.gradle'
build.gradle.kts
apply(from = "other.gradle.kts")

脚本插件会自动解决,可以从本地文件系统或远程位置的脚本中应用. 文件系统位置是相对于项目目录的,而远程脚本位置是通过HTTP URL指定的. 可以将多个脚本插件(任意一种形式)应用于给定目标.

Finding community plugins

Gradle有一个充满活力的插件开发人员社区,他们为各种功能贡献插件. Gradle 插件门户提供了一个用于搜索和浏览社区插件的界面.

More on plugins

本章旨在介绍插件和Gradle及其作用. 有关插件内部工作的更多信息,请参见" 自定义插件" .