第六十章. 初始化脚本

Chapter 60. Initialization Scripts

Gradle 提供了一种强大的机制,能让你根据当前环境自定义构建。这种机制还支持希望与 Gradle 集成的工具。
Gradle provides a powerful mechanism to allow customizing the build based on the current environment. This mechanism also supports tools that wish to integrate with Gradle.

60.1. 基本用法

60.1. Basic usage

初始化脚本类似于 Gradle 中的其他脚本,但是这些脚本在构建启动之前就运行。以下是几种可能的用法:
Initialization scripts (a.k.a. init scripts) are similar to other scripts in Gradle. These scripts, however, are run before the build starts. Here are several possible uses:

  • 设置一些企业范围的配置,比如在哪里查找自定义插件。

    Set up enterprise-wide configuration, such as where to find custom plugins.

  • 基于当前环境(比如开发人员的机器与持续集成服务器)来设置属性。

    Set up properties based on the current environment, such as a developer's machine vs. a continuous integration server.

  • 提供构建所需要的有关用户的个人信息,比如仓库或数据库认证凭证。

    Supply personal information about the user that is required by the build, such as repository or database authentication credentials.

  • 定义机器的一些特定信息,比如 JDK 的安装位置。

    Define machine specific details, such as where JDKs are installed.

  • 注册构建监听器。这对一些希望能够监听 Gradle 事件的外部工具会很有用。

    Register build listeners. External tools that wish to listen to Gradle events might find this useful.

  • 注册构建记录器。你可能希望自定义 Gradle 对生成的事件的日志打印。

    Register build loggers. You might wish to customize how Gradle logs the events that it generates.

初始化脚本的一个主要限制是,它们不能访问 buildSrc 项目里的类(该功能的详细信息可参见 第 59.3节,《的buildSrc 项目中的构建源代码》)。
One main limitation of init scripts is that they cannot access classes in the buildSrc project (see Section 59.3, “Build sources in the buildSrc project” for details of this feature).

60.2. 使用初始化脚本

60.2. Using an init script

使用一个初始化脚本的方法有几种:
There are several ways to use an init script:

  • 在命令行上指定文件。命令行选项为 -I--init-script,后跟脚本路径。这个命令行选项可以出现多次,每次在其后添加另一个 init 脚本。

    Specify a file on the command line. The command line option is -I or --init-script followed by the path to the script. The command line option can appear more than once, each time adding another init script.

  • 把一个 init.gradle 文件放到 USER_HOME/.gradle/ 目录。

    Put a file called init.gradle in the USER_HOME/.gradle/ directory.

  • 把一个文件名以 .gradle 结尾的文件放到 USER_HOME/.gradle/init.d/ 目录。

    Put a file that ends with .gradle in the USER_HOME/.gradle/init.d/ directory.

  • 把一个文件名以 .gradle 结尾的文件放到 Gradle 分发包的 GRADLE_HOME/init.d/ 目录。这让你能打包一个包含了某些自定义构建逻辑和插件的 Gradle 定制分发包。结合它和 Gradle wrapper,能使得自定义逻辑在企业中的所有构建中都可用。

    Put a file that ends with .gradle in the GRADLE_HOME/init.d/ directory, in the Gradle distribution. This allows you to package up a custom Gradle distribution containing some custom build logic and plugins. You can combine this with the Gradle wrapper as a way to make custom logic available to all builds in your enterprise.

如果 Gradle 找到了多个初始化脚本,那么将按照上面指定的顺序执行这些脚本。而给定目录的脚本,则按照字母顺序执行。这样使得一个工具可以在命令行上指定初始化脚本,并且用户可以放一个初始化脚本在他们的主目录中用于定义环境,而当 Gradle 执行时,这两个脚本会运行。
If more than one init script is found they will all be executed, in the order specified above. Scripts in a given directory are executed in alphabetical order. This allows, for example, a tool to specify an init script on the command line and the user to put one in their home directory for defining the environment and both scripts will run when Gradle is executed.

60.3. 写一个初始化脚本

60.3. Writing an init script

与 Gradle 构建脚本类似,初始化脚本也是一个 groovy 脚本。每个初始化脚本都有一个与之关联的 Gradle 实例,在这个初始化脚本中的任何属性引用和方法调用,都会委派给这个 Gradle 实例。
Similar to a Gradle build script, an init script is a groovy script. Each init script has a Gradle instance associated with it. Any property reference and method call in the init script will delegate to this Gradle instance.

每个初始化脚本也都实现了 Script 接口。
Each init script also implements the Script interface.

60.3.1. 从一个初始化脚本中配置项目

60.3.1. Configuring projects from an init script

你可以在构建中使用初始化脚本配置项目,类似于在多项目构建中配置项目。以下的例子展示了如何在项目被评估 之前 从初始化脚本中执行额外的配置。这个示例使用此功能来配置一个额外仅用于特定环境的仓库。
You can use an init script to configure the projects in the build. This works in a similar way to configuring projects in a multi-project build. The following sample shows how to perform extra configuration from an init script before the projects are evaluated. This sample uses this feature to configure an extra repository to be used only for certain environments.

示例 60.1. 使用初始化脚本在项目评估之前执行额外的配置 - Example 60.1. Using init script to perform extra configuration before projects are evaluated

build.gradle

repositories {
    mavenCentral()
}

task showRepos << {
    println "All repos:"
    println repositories.collect { it.name }
}

init.gradle

allprojects {
    repositories {
        mavenLocal()
    }
}

gradle --init-script init.gradle -q showRepos 的输出结果
Output of gradle --init-script init.gradle -q showRepos

> gradle --init-script init.gradle -q showRepos
All repos:
[MavenLocal, MavenRepo]

60.4. 初始化脚本的外部依赖

60.4. External dependencies for the init script

第 59.5 节,《构建脚本的外部依赖》 中,已经解释了如何向构建脚本添加外部依赖。初始化脚本同样可以定义外部依赖。通过使用 initscript() 方法,传入一个声明了初始化脚本类路径的闭包就可以了。
In Section 59.5, “External dependencies for the build script” is was explained how to add external dependencies to a build script. Init scripts can similarly have external dependencies defined. You do this using the initscript() method, passing in a closure which declares the init script classpath.

示例 60.2. 声明构建脚本的外部依赖 - Example 60.2. Declaring external dependencies for an init script

init.gradle

initscript {
    repositories {
        mavenCentral()
}
    }
    dependencies {
        classpath group: 'org.apache.commons', name: 'commons-math', version: '2.0'
    }
}

这个传给 initscript() 方法的闭包配置了一个 ScriptHandler 实例。你可以通过将依赖添加到 classpath 配置的方式来声明初始化脚本类路径。这与 Java 编译类路径的声明方式是一样的。你可以使用第 50.4 节,《如何声明你的依赖》中描述的除项目依赖之外的任何依赖类型。
The closure passed to the initscript() method configures a ScriptHandler instance. You declare the init script classpath by adding dependencies to the classpath configuration. This is the same way you declare, for example, the Java compilation classpath. You can use any of the dependency types described in Section 50.4, “How to declare your dependencies”, except project dependencies.

声明了初始化脚本类路径后,你可以在初始化脚本中使用这些类,就像使用其他在类路径上的类一样。以下示例是在上一示例的基础上,增加了使用初始化脚本类路径中的类。
Having declared the init script classpath, you can use the classes in your init script as you would any other classes on the classpath. The following example adds to the previous example, and uses classes from the init script classpath.

示例 60.3. 具有外部依赖的初始化脚本 - Example 60.3. An init script with external dependencies

init.gradle

import org.apache.commons.math.fraction.Fraction

initscript {
    repositories {
        mavenCentral()
}
    }
    dependencies {
        classpath group: 'org.apache.commons', name: 'commons-math', version: '2.0'
    }
}

println Fraction.ONE_FIFTH.multiply(2)

gradle --init-script init.gradle -q doNothing 的输出结果
Output of gradle --init-script init.gradle -q doNothing

> gradle --init-script init.gradle -q doNothing
2 / 5

60.5. 初始化脚本插件

60.5. Init script plugins

与 Gradle 构建脚本类似,插件也可以应用在初始化脚本上。
Similar to a Gradle build script or a Gradle settings file, plugins can be applied on init scripts.

示例 60.4. 在初始化脚本中使用插件 - Example 60.4. Using plugins in init scripts

init.gradle

apply plugin:EnterpriseRepositoryPlugin

class EnterpriseRepositoryPlugin implements Plugin<Gradle> {

    private static String ENTERPRISE_REPOSITORY_URL = "http://repo.gradle.org/gradle/repo"

    void apply(Gradle gradle) {
        // ONLY USE ENTERPRISE REPO FOR DEPENDENCIES
        gradle.allprojects{ project ->
            project.repositories {

                //remove all repositories not pointing to the enterprise repository url
                all { ArtifactRepository repo ->
                    if (!(repo instanceof MavenArtifactRepository) || repo.url.toString() != ENTERPRISE_REPOSITORY_URL) {
                        project.logger.lifecycle "Repository ${repo.url} removed. Only $ENTERPRISE_REPOSITORY_URL is allowed"
                        remove repo
                    }
                }

                // add the enterprise repository
                maven {
                    name "STANDARD_ENTERPRISE_REPO"
                    url ENTERPRISE_REPOSITORY_URL
                }
            }
        }
    }
}

build.gradle

repositories{
    mavenCentral()
}

 task showRepositories << {
    repositories.each{
        println "repository: ${it.name} ('${it.url}')"
    }
}

gradle -I init.gradle showRepositories 的输出结果
Output of gradle -q -I init.gradle showRepositories

> gradle -q -I init.gradle showRepositories
repository: STANDARD_ENTERPRISE_REPO ('http://repo.gradle.org/gradle/repo')

这个在示例的初始化脚本中的插件,确保了当执行构建时只使用一个指定的仓库。
The plugin in the sample init scripts ensures, that only a specified repository is used when running the build.

当在初始化脚本中应用插件时,Gradle 将实例化插件并调用插件实例的 Plugin.apply() 方法。然后 gradle 对象作为参数传入,用于配置构建的所有方面。当然,应用的插件也可以解析为外部依赖,如第 60.4 节,《初始化脚本的外部依赖》中所述。
When applying plugins within the init script, Gradle instantiates the plugin and calls the plugin instance's Plugin.apply() method. The gradle object is passed as a parameter, which can be used to configure all aspects of a build. Of course, the applied plugin can be resolved as external dependency as described in Section 60.4, “External dependencies for the init script”