隔离项目是 Gradle 的一个预 alpha 特性,它扩展了配置缓存以进一步提升性能,尤其是在 Android Studio 和 IDEA 同步方面的性能。

启用隔离项目后,Gradle 项目的配置模型将相互“隔离”。这意味着应用于某个项目的构建逻辑(例如构建脚本或插件)无法直接访问另一个项目的可变状态。这样可以安全地并行运行每个项目的配置和工具模型创建,并且每个项目的结果都可以独立地缓存和失效。

Gradle 8.11 时的状态

启用隔离项目后,Gradle 在 IDE 同步期间会应用两级缓存

  1. Gradle 首先应用粗粒度缓存。

    为此,Gradle 会缓存整个同步操作的结果,并在影响 IDE 模型的任何内容都没有更改时重用它。当缓存条目可以重用时,Gradle 会跳过整个同步操作,并将缓存结果返回给 IDE。

  2. 通常,settings 和 build 脚本会影响 IDE 模型,但项目的源代码不会。因此,当这些脚本发生更改时,缓存条目无法重用。发生这种情况时,Gradle 会退回到细粒度缓存。

    为此,Gradle 会缓存为每个项目创建工具模型的结果,并在影响这些模型的任何内容都没有更改时重用这些模型。当项目的缓存模型可以重用时,Gradle 会跳过该项目上的所有工作,包括配置阶段和依赖解析等其他工作。

这意味着 Gradle 只会为配置发生更改的项目配置和创建工具模型。这项工作会为每个项目并行完成。

当前限制

隔离项目是一个预 alpha 特性,因此目前的实现存在一些限制。这些将在未来的 Gradle 版本中解决

  • Gradle、IntelliJ IDEA、Android Studio 和 Kotlin 插件尚未与隔离项目完全兼容,因此您应该会看到一些违规报告。相关团队正在积极努力修复这些不兼容问题。

  • 并行配置尚不支持 按需配置。即使不运行任务,所有项目也会被配置。

  • 对包含构建的更改会使所有缓存结果失效,即使更改不会影响缓存结果。

  • 当前实现并未利用隔离来限制峰值内存消耗。目前,峰值内存消耗取决于需要配置的项目数量。

  • 所有缓存,包括配置缓存,都在本地机器上进行。尚不支持远程缓存。

如何使用?

您需要 Gradle 8.5 或更高版本才能使用隔离项目,最好是最近的 nightly 版本。您还应该使用最新版本的 IDEA 或 Android Studio。

此特性默认关闭。您可以通过将系统属性 org.gradle.unsafe.isolated-projects 设置为 true 来启用它。例如

$ gradle build -Dorg.gradle.unsafe.isolated-projects=true

启用后,只要构建逻辑尝试跨越项目边界访问另一个项目的模型,Gradle 就会使构建失败。Gradle 会像处理其他问题一样,在配置缓存报告中收集所有这些访问问题。

可以使用配置缓存命令行选项来控制 Gradle 如何处理这些问题。例如

  • 可以使用 --configuration-cache-problems=warn 将访问问题视为警告而不是错误。

  • 可以使用 -Dorg.gradle.configuration-cache.max-problems=x 来增加报告中包含的问题的最大数量。

当存在访问问题时,您还可以使用 -Dorg.gradle.internal.invalidate-coupled-projects=false 来强制进行并行配置。

请注意,启用隔离项目时,这些选项会禁用确保执行并行和缓存安全的验证,因此在使用它们时可能会看到一些意外行为。

构建逻辑约束

隔离项目阻止构建逻辑访问另一个项目的状态。这包括

  • 使用 Project 类型上的大多数方法。允许使用少数返回有关项目的不可变信息的方法

    • getName()

    • getPath()

    • getBuildTreePath()

    • getProjectDir()

    • getRootDir()

    • getChildProjects()

    • getSubprojects()

    • getAllProjects()

    • project() 重载

    • subprojects() 重载

    • allprojects() 重载

请注意,隔离项目是一个预 alpha 特性。这些约束并非最终确定,可能随时更改。

更新日志

Gradle 8.11

并行配置的项目

当从命令行构建(即执行任务)并启用隔离项目时,项目现在会并行配置。

并行配置尚不支持按需配置。即使没有任务要运行,所有项目也会被配置。如果这种权衡不合需要,并且您希望项目使用按需配置进行串行配置,请将系统属性 org.gradle.internal.isolated-projects.configure-on-demand.tasks 设置为 true

Gradle 8.9

解除对基于字符串的任务依赖表示法的限制

以字符串表示法依赖另一个项目的任务是一种常见用法

foo.dependsOn(":a:bar")

从这个版本开始,这不再被视为违反隔离项目边界的行为。

gradle init 生成与隔离项目兼容的项目

Build Init Plugin 支持创建多模块项目。

从这个版本开始,gradle init 生成的项目与隔离项目限制兼容。

IsolatedProject 在复合构建中提供项目标识符

IsolatedProject 类型在 Gradle 8.8 中引入,用于明确标记在跨项目访问时安全的项目状态。

Gradle 8.9 添加了一个 buildTreePath 成员,在 复合构建 设置中作为唯一的项目标识符。

Gradle 8.8

新的 Gradle 生命周期回调

此版本引入了一个新的 GradleLifecycle API,可通过 gradle.lifecycle 访问,插件作者和构建工程师可以使用它来注册在构建生命周期的特定点执行的操作。

注册为 GradleLifecycle 回调的操作(目前是 beforeProjectafterProject)是 隔离的,它们在对每个项目私有的隔离上下文中运行。这将允许 Gradle 执行额外的性能优化,并且将来是利用构建配置阶段并行化的必要条件。

虽然现有的回调继续工作,我们鼓励大家采用新的 API 并向我们提供早期反馈。

下面的示例展示了如何在 settings 脚本或 settings 插件中使用这个新 API 来将配置应用于所有项目,同时避免 跨项目配置

settings.gradle.kts
include("sub1")
include("sub2")

gradle.lifecycle.beforeProject {
    apply(plugin = "base")
    repositories {
        mavenCentral()
    }
}

隔离的项目视图

现在支持通过 Project.getIsolated() 获取项目的隔离视图,表示为 IsolatedProject

该视图仅公开那些在并行运行构建配置阶段时(将在未来版本中支持)跨项目边界安全访问的属性。

下面的示例展示了如何从 Project 配置回调中使用该 API 以并行安全的方式查询根项目目录

gradle.lifecycle.beforeProject {
    val rootDir = project.isolated.rootProject.projectDirectory
    println("The root project directory is $rootDir")
}