第二十一章. Gradle 插件

Chapter 21. Gradle Plugins

Gradle 在它的核心中有意地提供了一些小但有用的功能,用于在真实世界中的自动化。所有有用的功能,例如以能够编译 Java 代码为例,都是通过 插件 进行添加的。插件添加了新任务(例如 JavaCompile ),域对象(例如 SourceSet ),约定(例如主要的 Java 源代码是位于 src/main/java),以及扩展的核心对象和其他插件的对象。
Gradle at its core intentionally provides little useful functionality for real world automation. All of the useful features, such as the ability to compile Java code for example, are added by plugins . Plugins add new tasks (e.g. JavaCompile ), domain objects (e.g. SourceSet ), conventions (e.g. main Java source is located at src/main/java ) as well as extending core objects and objects from other plugins.

在这一章中,我们将讨论如何使用插件以及术语和插件相关的概念。
In this chapter we will discuss how to use plugins and the terminology and concepts surrounding plugins.

21.1. 应用插件

21.1. Applying plugins

插件都认为是被 应用 ,通过 Project.apply() 方法来完成。
Plugins are said to be applied , which is done via the Project.apply() method.

示例 21.1. 应用插件
Example 21.1. Applying a plugin

build.gradle

apply plugin: 'java'

插件都有表示它们自己的一个短名称。. 在上述例子中,我们使用短名称 java去应用 JavaPlugin
Plugins advertise a short name for themselves. In the above case, we are using the short name ‘ java ’ to apply the JavaPlugin .

我们还可以使用下面的语法:
We could also have used the following syntax:

示例 21.2. 通过类型应用插件
Example 21.2. Applying a plugin by type

build.gradle

apply plugin: org.gradle.api.plugins.JavaPlugin

由于 Gradle 的默认导入 (见 附录 E, 现有的 IDE 支持和如何应对不支持的情况),您还可以这样写:
Thanks to Gradle's default imports (see Appendix E, Existing IDE Support and how to cope without it ) you could also write:

示例 21.3. 通过类型应用插件
Example 21.3. Applying a plugin by type

build.gradle

apply plugin: JavaPlugin

插件的应用是 幂等的 。也就是说,一个插件可以被应用多次。如果以前已应用了该插件,任何进一步的应用都不会再有任何效果。
The application of plugins is idempotent . That is, a plugin can be applied multiple times. If the plugin has previously been applied, any further applications will have no effect.

一个插件是任何实现了 Plugin 接口的简单的类。Gradle 提供了核心插件作为其发行包的一部分,所以简单地应用如上插件是你所需要做的。然而,对于第三方插件,你需要进行配置以使插件在构建类路径中可用。有关如何进行此操作的详细信息,请参阅 59.5章节,“构建脚本的外部依赖”
A plugin is simply any class that implements the Plugin interface. Gradle provides the core plugins as part of its distribution so simply applying the plugin as above is all you need to do. For 3rd party plugins however, you need to make the plugins available to the build classpath. For more information on how to do this, see Section 59.5, “External dependencies for the build script” .

关于编写自己的插件的详细信息,请参阅 58章节, 编写自定义插件
For more on writing your own plugins, see Chapter 58, Writing Custom Plugins .

21.2. 插件都做了什么

21.2. What plugins do

把插件应用到项目中可以让插件来扩展项目的功能。它可以做的事情如:
Applying a plugin to the project allows the plugin to extend the project's capabilities. It can do things such as:

  • 将任务添加到项目 (如编译、 测试)
    Add tasks to the project (e.g. compile, test)
  • 使用有用的默认设置对已添加的任务进行预配置。
    Pre-configure added tasks with useful defaults.
  • 向项目中添加依赖配置 (见 第 8.3 节,“依赖配置”)。
    Add dependency configurations to the project (see Section 8.3, “Dependency configurations” ).
  • 通过扩展对现有类型添加新的属性和方法。
    Add new properties and methods to existing type via extensions.

让我们来看看:
Let's check this out:

示例 21.4. 通过插件添加任务
Example 21.4. Tasks added by a plugin

build.gradle

apply plugin: 'java'

task show << {
    println relativePath(compileJava.destinationDir)
    println relativePath(processResources.destinationDir)
}

gradle -q show 的输出结果
Output of gradle -q show

> gradle -q show
build/classes/main
build/resources/main

Java 插件已经向项目添加了 compileJava任务和 processResources任务,并且配置了这两个任务的 destinationDir属性。
The Java plugin has added a compileJava task and a processResources task to the project and configured the destinationDir property of both of these tasks.

21.3. 约定

21.3. Conventions

插件可以通过智能的方法对项目进行预配置以支持约定优于配置。Gradle 对此提供了机制和完善的支持,而它是强大-然而-简洁的构建脚本中的一个关键因素。
Plugins can pre-configure the project in smart ways to support convention-over-configuration. Gradle provides mechanisms and sophisticated support and it's a key ingredient in powerful-yet-concise build scripts.

在上面的示例中我们看到,Java 插件添加了一个任务,名字为 compileJava ,有一个名为 destinationDir 的属性(即配置编译的 Java 代码存放的地方)。Java 插件默认此属性指向项目目录中的 build/classes/main。这是通过一个 合理的默认 的约定优于配置的例子。
We saw in the example above that the Java plugins adds a task named compileJava that has a property named destinationDir (that configures where the compiled Java source should be placed). The Java plugin defaults this property to point to build/classes/main in the project directory. This is an example of convention-over-configuration via a reasonable default .

我们可以简单地通过给它一个新的值来更改此属性。
We can change this property simply by giving it a new value.

示例 21.5. 更改插件的默认设置
Example 21.5. Changing plugin defaults

build.gradle

apply plugin: 'java'

compileJava.destinationDir = file("$buildDir/output/classes")

task show << {
    println relativePath(compileJava.destinationDir)
}

gradle -q show 的输出结果
Output of gradle -q show

> gradle -q show
build/output/classes

然而, compileJava任务很可能不是唯 一需要知道类文件在哪里的任务。
However, the compileJava task is likely to not be the only task that needs to know where the class files are.

Java 插件添加了 source sets 的概念 (见 SourceSet ) 来描述的源文件集的各个方面,其中一个方面是在编译的时候这些类文件应该被写到哪个地方。Java 插件将 compileJava任务的 destinationDir属性映射到源文件集的这一个方面。
The Java plugin adds the concept of source sets (see SourceSet ) to describe the aspects of a set of source, one aspect being where the class files should be written to when it is compiled. The Java plugin maps the destinationDir property of the compileJava task to this aspect of the source set.

我们可以通过这个源码集修改写入类文件的位置。
We can change where the class files are written via the source set.

示例 21.6. 插件中的约定对象
Example 21.6. Plugin convention object

build.gradle

apply plugin: 'java'

sourceSets.main.output.classesDir = file("$buildDir/output/classes")

task show << {
    println relativePath(compileJava.destinationDir)
}

gradle -q show 的输出结果
Output of gradle -q show

> gradle -q show
build/output/classes

在上面的示例中,我们应用 Java 插件,除其他外,还做了下列操作:
In the example above, we applied the Java plugin which, among other things, did the following:

  • 添加了一个新的域对象类型: SourceSet
    Added a new domain object type: SourceSet
  • 通过属性的默认(即常规)配置了 main 源码集
    Configured a main source set with default (i.e. conventional) values for properties
  • 配置支持使用这些属性来执行工作的任务
    Configured supporting tasks to use these properties to perform work

所有这一切都发生在 apply plugin: "java"这一步过程中。在上面例子中,我们在约定配置被执行之后, 修改了 类文件所需的位置。在上面的示例中可以注意到, compileJava.destinationDir的值也被修改了,以反映出配置的修改。
All of this happened during the apply plugin: "java" step. In the example above, we changed the desired location of the class files after this conventional configuration had been performed. Notice by the output with the example that the value for compileJava.destinationDir also changed to reflect the configuration change.

考虑一下另一种消费类文件的任务的情况。如果这个任务使用 sourceSets.main.output.classesDir的值来配置,那么修改了这个位置的值,无论它是什么时候被修改,将同时更新 compileJava任务和这一个消费者任务。
Consider the case where another task is to consume the classes files. If this task is configured to use the value from sourceSets.main.output.classesDir , then changing it in this location will update both the compileJava task and this other consumer task whenever it is changed.

这种配置对象的属性以在所有时间内(甚至当它更改的时候)反映另一个对象的任务的值的能力被称为“ 映射约定 ”。它可以令 Gradle 通过约定优于配置及合理的默认值来实现简洁的配置方式。而且,如果默认约定需要进行修改时,也不需要进行完全的重新配置。如果没有这一点,在上面的例子中,我们将不得不重新配置需要使用类文件的每个对象。
This ability to configure properties of objects to reflect the value of another object's task at all times (i.e. even when it changes) is known as “ convention mapping ”. It allows Gradle to provide conciseness through convention-over-configuration and sensible defaults yet not require complete reconfiguration if a conventional default needs to be changed. Without this, in the example above, we would have had to reconfigure every object that needs to work with the class files.

21.4. 更多关于插件的内容

21.4. More on plugins

这一章旨在作为对插件和 Gradle 及他们扮演的角色的导言。关于插件的内部运作的详细信息,请参阅 第 58 章, 编写自定义插件
This chapter aims to serve as an introduction to plugins and Gradle and the role they play. For more information on the inner workings of plugins, see Chapter 58, Writing Custom Plugins .