第六十章. 初始化脚本

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

初始化脚本 (也称为init scripts和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.

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


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

init 脚本的一个主要限制是,它们不能访问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. 使用 init 脚本

60.2. Using an init script

有几种方法来使用 init 脚本︰
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.

  • 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找到了多个init脚本,那么它们将会全部按照以上的顺序执行。而给定目录的脚本,会按照字母顺序执行。这能够使得,比如,一个工具可以在命令行上指定一个init 脚本,并且用户可以放一个初始化脚本到他们的用户目录下用于定义环境,而当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. 写一个 init 脚本

60.3. Writing an init script

与Gradle构建脚本类似,init 脚本也是一个groovy 脚本。每一个init 脚本都有一个 Gradle 实例与其关联。在这个init 脚本中调用的任何属性引用以及方法,都会委托给这个 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

你可以使用一个init 脚本来在构建中配置项目。这与在多项目构建中配置项目的方式类似。下面的例子展示了如何在项目被评估 之前 从一个init 脚本中执行另外的配置。该示例使用此功能来配置额外的仓库,以让它仅用于特定的环境。
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. 使用 init 脚本在项目评估之前执行额外的配置 - 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. init 脚本的外部依赖

60.4. External dependencies for the init script

第 59.5节,“构建脚本的外部依赖” 中已经解释了,如何向一个构建脚本添加外部依赖。init 脚本同样也可以定义外部依赖。你可以通过使用<c0>initscript()</c0>方法,传入一个定义init 脚本的classpath的闭包。
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 配置来定义init 脚本的classpath。这和你定义Java编译classpath是同样的。你可以使用在 第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.

声明了init 脚本的classpath之后,你可以使用init 脚本中的类,就像classpath上的任何其他类一样。下面的示例将添加到前面的示例中,并使用init 脚本classpath中的类。
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. 具有外部依赖的init 脚本 - 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)

Output of gradle --init-script init.gradle -q doNothing

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

60.5. Init 脚本插件

60.5. Init script plugins

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

示例 60.4. 在 init 脚本中使用插件 - 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')

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

当把插件应用在 init 脚本中时,Gradle 会实例化该插件,并调用这个插件的实例的Plugin.apply()方法。然后 gradle 对象会被作为参数传入,用于配置构建的所有方面。当然,所应用的插件也可以作为第 60.4节,“init 脚本外部依赖”中所述的外部依赖被解析。
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”