隔离项目(Isolated Projects)是一个预 Alpha 阶段的 Gradle 特性,它扩展了配置缓存以进一步提高性能,特别是 Android Studio 和 IDEA 同步的性能。

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

Gradle 8.11 中的状态

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

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

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

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

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

这意味着 Gradle 只会配置并创建其配置已更改的项目的工具模型。这项工作针对每个项目并行完成。

当前限制

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

  • Gradle、IntelliJ IDEA、Android Studio 和 Kotlin 插件尚未与隔离项目 100% 兼容,因此您可能会看到一些违规报告。团队正在积极解决这些不兼容问题。

  • 并行配置不支持按需配置(Configuration on Demand)。所有项目都将被配置,即使它们没有运行任务。

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

  • 该实现没有利用隔离来限制峰值内存消耗。目前,峰值内存消耗是需要配置的项目数量的函数。

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

如何使用?

您需要 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 生成兼容隔离项目的功能

构建初始化插件支持创建多模块项目。

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

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

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

Gradle 8.9 添加了 buildTreePath 成员,它在组合构建设置中充当唯一的项目标识符。

Gradle 8.8

新的 Gradle 生命周期回调

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

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

虽然现有回调仍然有效,但我们鼓励大家采用新的 API 并向我们提供早期反馈。

下面的示例展示了如何在设置脚本或设置插件中使用此新 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")
}