初始化脚本是在构建脚本执行之前运行的脚本。它们允许你在构建早期定制构建环境或配置设置。

初始化脚本对于在多个项目之间设置常见配置(例如仓库、插件或自定义任务)非常有用。

使用初始化脚本

初始化脚本,也称为 init 脚本,类似于 Gradle 中的其他脚本。初始化脚本在构建开始前运行。

它们可用于多种目的

  • 设置企业范围的配置(例如,自定义插件位置)

  • 根据环境配置属性(例如,开发人员机器与 CI 服务器)

  • 提供特定于用户的信息(例如,身份验证凭据)

  • 定义特定于机器的详细信息(例如,JDK 位置)

  • 注册构建监听器(例如,希望监听 Gradle 事件的外部工具可能会觉得这很有用)

  • 注册日志记录器(例如,自定义 Gradle 如何记录它生成的事件)

init 脚本的一个主要限制是它们无法访问 buildSrc 项目中的类。

调用初始化脚本

有几种方法可以调用 init 脚本(按优先级排序)

  1. 在命令行上指定文件,使用选项 -I--init-script,后跟脚本的路径。

    命令行选项可以出现多次,每次都会添加另一个 init 脚本。如果命令行上指定的任何文件不存在,构建将失败。

  2. 将名为 init.gradle(.kts) 的文件放在 $GRADLE_USER_HOME/ 目录中。

  3. 将名为 yourfilename.init.gradle(.kts) 的文件放在 $GRADLE_USER_HOME/init.d/ 目录中。

  4. 将名为 yourfilename.init.gradle(.kts) 的文件放在 $GRADLE_HOME/init.d/ 目录中。条目将按字母顺序评估。

    这允许你打包一个包含自定义构建逻辑和插件的定制 Gradle 分发包。你可以将其与Gradle wrapper结合使用,使自定义逻辑对企业中的所有构建可用。

如果找到多个 init 脚本,它们将按上面指定的顺序全部执行。

给定目录中的脚本按字母顺序执行。例如,工具可以在命令行上指定一个 init 脚本,并在主目录中指定另一个来定义环境。当 Gradle 执行时,这两个脚本都会运行。

编写初始化脚本

与 Gradle 构建脚本一样,init 脚本是 Groovy 或 Kotlin 脚本。每个 init 脚本都有一个与之关联的 Gradle 实例。init 脚本中的任何属性引用和方法调用都将委托给这个 Gradle 实例。

每个 init 脚本都实现了 Script 接口。

编写 init 脚本时,请注意你要访问的引用的作用域。例如,从 gradle.properties 加载的属性可在 SettingsProject 实例上访问,但在 Gradle 实例上不可用。

从初始化脚本配置项目

你可以使用 init 脚本来配置构建中的项目。这与在多项目构建中配置项目类似。

以下示例展示了如何在项目评估之前从 init 脚本执行额外配置

build.gradle
repositories {
    mavenCentral()
}

tasks.register('showRepos') {
    def repositoryNames = repositories.collect { it.name }
    doLast {
        println "All repos:"
        println repositoryNames
    }
}
init.gradle
allprojects {
    repositories {
        mavenLocal()
    }
}
build.gradle.kts
repositories {
    mavenCentral()
}

tasks.register("showRepos") {
    val repositoryNames = repositories.map { it.name }
    doLast {
        println("All repos:")
        println(repositoryNames)
    }
}
init.gradle.kts
allprojects {
    repositories {
        mavenLocal()
    }
}

此示例使用此特性来配置一个仅用于特定环境的额外仓库。

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

添加外部依赖项

Init 脚本还可以使用 initscript() 方法声明依赖项,该方法传入一个声明 init 脚本类路径的闭包。

声明 init 脚本的外部依赖项

init.gradle.kts
initscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.apache.commons:commons-math:2.0")
    }
}
init.gradle
initscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.apache.commons:commons-math:2.0'
    }
}

传递给 initscript() 方法的闭包配置一个 ScriptHandler 实例。你可以通过向 classpath 配置添加依赖项来声明 init 脚本类路径。

这与你声明 Java 编译类路径的方式相同。你可以使用声明依赖项中描述的任何依赖类型,除了项目依赖项。

声明了 init 脚本类路径后,你就可以像使用类路径上的任何其他类一样使用 init 脚本中的类。以下示例在前一个示例的基础上增加了内容,并使用了来自 init 脚本类路径的类。

带有外部依赖项的 init 脚本

init.gradle.kts
import org.apache.commons.math.fraction.Fraction

initscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.apache.commons:commons-math:2.0")
    }
}

println(Fraction.ONE_FIFTH.multiply(2))
build.gradle.kts
tasks.register("doNothing")
init.gradle
import org.apache.commons.math.fraction.Fraction

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

println Fraction.ONE_FIFTH.multiply(2)
build.gradle
tasks.register('doNothing')
> gradle --init-script init.gradle.kts -q doNothing
2 / 5
> gradle --init-script init.gradle -q doNothing
2 / 5

应用插件

插件可以应用于 init 脚本,就像应用于 Gradle 构建脚本或 Gradle settings 文件一样。

在 init 脚本中使用插件

init.gradle.kts
apply<EnterpriseRepositoryPlugin>()

class EnterpriseRepositoryPlugin : Plugin<Gradle> {
    companion object {
        const val ENTERPRISE_REPOSITORY_URL = "https://repo.gradle.org/gradle/repo"
    }

    override fun apply(gradle: Gradle) {
        // ONLY USE ENTERPRISE REPO FOR DEPENDENCIES
        gradle.allprojects {
            repositories {

                // Remove all repositories not pointing to the enterprise repository url
                all {
                    if (this !is MavenArtifactRepository || url.toString() != ENTERPRISE_REPOSITORY_URL) {
                        project.logger.lifecycle("Repository ${(this as? MavenArtifactRepository)?.url ?: name} removed. Only $ENTERPRISE_REPOSITORY_URL is allowed")
                        remove(this)
                    }
                }

                // add the enterprise repository
                add(maven {
                    name = "STANDARD_ENTERPRISE_REPO"
                    url = uri(ENTERPRISE_REPOSITORY_URL)
                })
            }
        }
    }
}
build.gradle.kts
repositories{
    mavenCentral()
}

data class RepositoryData(val name: String, val url: URI)

tasks.register("showRepositories") {
    val repositoryData = repositories.withType<MavenArtifactRepository>().map { RepositoryData(it.name, it.url) }
    doLast {
        repositoryData.forEach {
            println("repository: ${it.name} ('${it.url}')")
        }
    }
}
init.gradle
apply plugin: EnterpriseRepositoryPlugin

class EnterpriseRepositoryPlugin implements Plugin<Gradle> {

    private static String ENTERPRISE_REPOSITORY_URL = "https://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()
}

@Immutable
class RepositoryData {
    String name
    URI url
}

tasks.register('showRepositories') {
    def repositoryData = repositories.collect { new RepositoryData(it.name, it.url) }
    doLast {
        repositoryData.each {
            println "repository: ${it.name} ('${it.url}')"
        }
    }
}
> gradle --init-script init.gradle.kts -q showRepositories
repository: STANDARD_ENTERPRISE_REPO ('https://repo.gradle.org/gradle/repo')
> gradle --init-script init.gradle -q showRepositories
repository: STANDARD_ENTERPRISE_REPO ('https://repo.gradle.org/gradle/repo')

init 脚本中的插件确保在运行构建时只使用指定的仓库。

在 init 脚本中应用插件时,Gradle 会实例化该插件并调用该插件实例的 Plugin.apply(T) 方法。

gradle 对象作为参数传递,可用于配置构建的所有方面。当然,应用的插件可以作为外部依赖项解析,如init 脚本的外部依赖项中所述。