Gradle 提供了一种强大的机制,可根据当前环境自定义构建。

此机制还支持希望与 Gradle 集成的工具。

基本用法

初始化脚本(又称init 脚本)类似于 Gradle 中的其他脚本。然而,这些脚本在构建开始之前运行。

以下是几种可能的用途

  • 设置企业级配置,例如查找自定义插件的位置。

  • 根据当前环境(例如开发人员的机器与持续集成服务器)设置属性。

  • 提供构建所需的有关用户的个人信息,例如存储库或数据库身份验证凭据。

  • 定义特定于机器的详细信息,例如安装 JDK 的位置。

  • 注册构建侦听器。希望侦听 Gradle 事件的外部工具可能会发现这很有用。

  • 注册构建记录器。您可以自定义 Gradle 记录其生成的事件的方式。

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

使用 init 脚本

有几种方法可以使用 init 脚本

  • 在命令行上指定一个文件。命令行选项是-I--init-script,后跟脚本的路径。

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

  • 将名为 init.gradle(或 Kotlin 的 init.gradle.kts)的文件放入 $GRADLE_USER_HOME/ 目录中。

  • 将以 .gradle 结尾(或 Kotlin 的 .init.gradle.kts)的文件放入 $GRADLE_USER_HOME/init.d/ 目录中。

  • 将以 .gradle 结尾(或 Kotlin 的 .init.gradle.kts)的文件放入 Gradle 发行版中的 $GRADLE_HOME/init.d/ 目录中。

    这允许你打包一个包含自定义构建逻辑和插件的自定义 Gradle 发行版。你可以将此与 Gradle 包装器 结合使用,以便为企业中的所有构建提供自定义逻辑。

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

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

编写初始化脚本

与 Gradle 构建脚本类似,初始化脚本是一个 Groovy 或 Kotlin 脚本。每个初始化脚本都与一个 Gradle 实例相关联。初始化脚本中的任何属性引用和方法调用都将委托给此 Gradle 实例。

每个初始化脚本还实现 Script 接口。

在编写初始化脚本时,请注意你尝试访问的引用的范围。例如,从 gradle.properties 加载的属性在 SettingsProject 实例上可用,但在 Gradle 实例上不可用。

从初始化脚本配置项目

您可以使用初始化脚本来配置构建中的项目。这与在多项目构建中配置项目类似。

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

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()
    }
}
build.gradle
repositories {
    mavenCentral()
}

tasks.register('showRepos') {
    def repositoryNames = repositories.collect { it.name }
    doLast {
        println "All repos:"
        println repositoryNames
    }
}
init.gradle
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]

初始化脚本的外部依赖项

初始化脚本还可以使用 initscript() 方法声明依赖项,并传入声明初始化脚本类路径的闭包。

为初始化脚本声明外部依赖项

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 配置添加依赖项来声明初始化脚本类路径。

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

声明初始化脚本类路径后,您可以像使用类路径上的任何其他类一样在初始化脚本中使用这些类。以下示例添加到前面的示例中,并使用了初始化脚本类路径中的类。

具有外部依赖项的初始化脚本

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

初始化脚本插件

与 Gradle 构建脚本或 Gradle 设置文件类似,可以将插件应用到初始化脚本。

在初始化脚本中使用插件

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')

初始化脚本中的插件确保在运行构建时仅使用指定的存储库。

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

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