隔离项目是一项预 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% 与隔离项目兼容,因此您应该预期会看到一些违规报告。各个团队正在积极解决这些不兼容问题。

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

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

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

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

我该如何使用它?

您需要 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 插件 支持创建多模块项目。

从这个版本开始,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")
}