// Copyright (C) 2023 Gradle, Inc.
//
// Licensed under the Creative Commons Attribution-Noncommercial-ShareAlike 4.0 International License.;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://creativecommons.org/licenses/by-nc-sa/4.0/
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

:gradle-issues: https://github.com/gradle/gradle/issues/

= Isolated Projects

Isolated Projects is a pre-alpha Gradle feature that extends the configuration cache to further improve performance, particularly the performance of Android Studio and IDEA sync.

When Isolated Projects is enabled, the configuration model of Gradle projects are "isolated" from each other.
This means that build logic, such as build scripts or plugins, applied to a project cannot directly access the mutable state of another project.
This allows configuration and tooling model creation for each project to safely run in parallel, with the result cached and invalidated independently for each project.

== Status as of Gradle 8.9

When Isolated Projects is enabled, Gradle applies two levels of caching during IDE sync:

1. Gradle starts by applying coarse-grained caching.
+
To do this, Gradle caches the result of the entire sync operation and reuses it when nothing that affects the IDE model has changed.
When the cache entry can be reused, Gradle short-circuits the entire sync operation and returns the cached result to the IDE.

2. Generally, the settings and build scripts affect the IDE model, but the project's source code does not.
So, when these scripts change, the cache entry cannot be reused.
When this happens, Gradle falls back to fine-grained caching.
+
To do this, Gradle caches the result of creating the tooling models for each project and reuses these models when nothing that affects them has changed.
When a project's cached models can be reused, Gradle short-circuits all work on that project, including the configuration phase and other work such as dependency resolution.

This means that Gradle will only configure and create tooling models for projects whose configuration has changed.
This work is done in parallel for each project.

== Current limitations

Isolated Projects is a pre-alpha feature, and as such, the current implementation has a number of limitations.
These will be addressed in future Gradle releases:

* Gradle, IntelliJ IDEA, Android Studio, and Kotlin plugins are not yet 100% compatible with Isolated Projects, so you should expect to see some violations reported. The teams are actively working on fixing these incompatibilities.
* Parallel configuration and fine-grained caching are not applied to task execution. They are only applied to IDE sync.
* Changes to included builds invalidate all cached results, even when the change would not affect the cached results.
* The implementation does not exploit the isolation to limit peak memory consumption. Currently, peak memory consumption is a function of how many projects must be configured.
* All caching, including the configuration cache, is done on the local machine. Remote caching is not supported yet.

== How do I use it?

You will need Gradle 8.5 or later to use Isolated Projects, preferably a recent nightly.
You should also use the most recent version of IDEA or Android Studio.

The feature is off by default.
You can enable it by setting the `org.gradle.unsafe.isolated-projects` system property to `true`.
For example:

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

When enabled, Gradle will fail the build whenever build logic attempts to cross project boundaries and access the model of another project.
Gradle collects all of these access problems in the configuration cache report, as it does other problems.

The configuration cache command-line options can be used to control how Gradle handles these problems. For example:

* `--configuration-cache-problems=warn` can be used to treat access problems as warnings instead of errors.
* `-Dorg.gradle.configuration-cache.max-problems=x` can be used to increase the maximum number of problems included in the report.

You can also use `-Dorg.gradle.internal.invalidate-coupled-projects=false` to force parallel configuration when there are access problems.

Note that these options disable the validation that makes execution parallel- and caching-safe when Isolated Projects is enabled, so you may see some unexpected behavior when using them.

== Build logic constraints

Isolated Projects prevents build logic from accessing the state of another project.
This includes:

* Using most methods on the `Project` type. A small number of methods that return immutable information about the project are allowed:
** `getName()`
** `getPath()`
** `getBuildTreePath()`
** `getProjectDir()`
** `getRootDir()`
** `getChildProjects()`
** `getSubprojects()`
** `getAllProjects()`
** `project()` overloads
** `subprojects()` overloads
** `allprojects()` overloads

Note that Isolated Projects is a pre-alpha feature.
These constraints are not final and can change at any time.

== ChangeLog

=== Gradle 8.9

==== Lift restriction on string-based task dependency notation

Depending on a task from another project in string-notated form is a common idiom:

```
foo.dependsOn(":a:bar")
```

Starting with this release, this is no longer considered a violation of Isolated Projects boundaries.

==== `gradle init` generates Isolated Projects compatible projects

The <<build_init_plugin.adoc#build_init_plugin, Build Init Plugin>> supports creating multi-module projects.

Starting with this release, `gradle init` generates projects compatible with Isolated Projects restrictions.

==== `IsolatedProject` provides a project identifier in composite builds

The link:{javadocPath}/org/gradle/api/project/IsolatedProject.html[IsolatedProject] type was introduced in Gradle 8.8 to explicitly mark the project state that is safe to access across projects.

Gradle 8.9 adds a link:{javadocPath}/org/gradle/api/project/IsolatedProject.html#getBuildTreePath()[`buildTreePath`] member, which serves as a unique project identifier in <<composite_builds.adoc#composite_builds, composite build>> setups.

=== Gradle 8.8

==== New Gradle lifecycle callbacks

This release introduces a new link:{javadocPath}/org/gradle/api/invocation/GradleLifecycle.html[`GradleLifecycle`] API, accessible via `gradle.lifecycle`, which plugin authors and build engineers can use to register actions to be executed at certain points in the build lifecycle.

Actions registered as `GradleLifecycle` callbacks (currently, `beforeProject` and `afterProject`) are *link:{javadocPath}/org/gradle/api/IsolatedAction.html)[isolated]*, running in an isolated context that is private to every project.
This will allow Gradle to perform additional performance optimizations and will be required in the future to take advantage of parallelism during the build configuration phase.

While the existing callbacks continue to work, we encourage everyone to adopt the new API and provide us with early feedback.

The example below shows how this new API could be used in a settings script or <<custom_plugins.adoc#project_vs_settings_vs_init_plugins, settings plugins>> to apply configuration to all projects,
while avoiding <<sharing_build_logic_between_subprojects.adoc#sec:convention_plugins_vs_cross_configuration, cross-project configuration>>:

[source,kotlin]
.settings.gradle.kts
----
include("sub1")
include("sub2")

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

==== Isolated project views

There is now support for obtaining an isolated view of a project as an   link:{javadocPath}org/gradle/api/project/IsolatedProject.html[`IsolatedProject`] via link:{javadocPath}/org/gradle/api/Project.html#getIsolated--[`Project.getIsolated()`].

The view exposes only those properties that are safe to access across project boundaries when running the build configuration phase in parallel (to be supported in a future release).

The example below shows how the API could be used from a `Project` configuration callback to query the root project directory in a parallel-safe way:

[source,kotlin]
----
gradle.lifecycle.beforeProject {
    val rootDir = project.isolated.rootProject.projectDirectory
    println("The root project directory is $rootDir")
}
----
