From bdbd5ba0f7ead1b68673deb53ad75c509470e05e Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 24 Jun 2026 02:20:33 +0000 Subject: [PATCH 1/2] build(gradle): enable configuration cache, drop configuration-on-demand Turn on org.gradle.configuration-cache and remove the now-superseded org.gradle.configureondemand (configuration-on-demand is incompatible with the configuration cache). Builds on the cross-project config removal from #971, which cleared the root-script barriers to a cacheable configuration phase. Validated locally: full configuration of all modules succeeds with zero configuration-cache problems, the entry is reused across runs, and a module unit test executes and reuses the cache. Problems fail the build by default, so CI enforces a clean configuration phase. This is also the prerequisite for enabling Isolated Projects later. --- gradle.properties | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index d88faa8ee..e57b45e0e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,10 +15,12 @@ org.gradle.parallel=true # Improves build performance by avoiding redundant task execution # More details: https://docs.gradle.org/current/userguide/build_cache.html org.gradle.caching=true -# Enables configuration on demand, making Gradle configure only necessary projects -# Speeds up builds in multi-project setups by skipping unneeded configurations -# More details: https://docs.gradle.org/current/userguide/multi_project_builds.html#sec:configuration_on_demand -org.gradle.configureondemand=true +# Enables the configuration cache: caches the result of the configuration phase +# and reuses it across builds, configuring projects in parallel and in isolation. +# This supersedes configuration-on-demand (which is incompatible with it). +# Problems fail the build by default, so CI enforces a clean configuration phase. +# More details: https://docs.gradle.org/current/userguide/configuration_cache.html +org.gradle.configuration-cache=true # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app"s APK # https://developer.android.com/topic/libraries/support-library/androidx-rn From a64e3b9c0db543640a5e74f97d636909671a2254 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 24 Jun 2026 02:31:35 +0000 Subject: [PATCH 2/2] build(gradle): make root-dir access Isolated Projects-safe Replace cross-project root access that Isolated Projects forbids with the IP-safe equivalents, without changing behavior: - compose convention plugin & app: rootProject.layout.projectDirectory -> isolated.rootProject.projectDirectory for the stability config file. - ContributorsSignatory: take the root directory as a File instead of a Project, so callers no longer invoke Project.file on the root project. Path resolution is unchanged (relative against root, absolute as-is). Verified under the configuration cache (already enabled): full configuration succeeds with zero problems and the entry reuses cleanly. With these in place, flipping org.gradle.unsafe.isolated-projects drops from 356 problems to a single remaining AGP-internal access in com.android.application, which is upstream and will clear with a future AGP that fully supports Isolated Projects. The IP flag stays off until then. --- apps/flipcash/app/build.gradle.kts | 5 ++-- apps/flipcash/benchmark/build.gradle.kts | 2 +- .../AndroidLibraryComposeConventionPlugin.kt | 6 ++++- .../src/main/java/ContributorsSignatory.kt | 27 ++++++++++--------- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/apps/flipcash/app/build.gradle.kts b/apps/flipcash/app/build.gradle.kts index 891bbe127..c3a25f7a0 100644 --- a/apps/flipcash/app/build.gradle.kts +++ b/apps/flipcash/app/build.gradle.kts @@ -24,7 +24,7 @@ fun gitVersionCode(): Int { return result.toInt().also { println("VersionCode $it") } } -val contributorsSigningConfig = ContributorsSignatory(rootProject) +val contributorsSigningConfig = ContributorsSignatory(rootDir) val appNamespace = "${Gradle.flipcashNamespace}.app.android" android { @@ -125,8 +125,9 @@ bugsnag { } composeCompiler { + // Isolated Projects-safe root access (see AndroidLibraryComposeConventionPlugin). stabilityConfigurationFile.set( - rootProject.layout.projectDirectory.file("compose_compiler_config.conf") + isolated.rootProject.projectDirectory.file("compose_compiler_config.conf") ) } diff --git a/apps/flipcash/benchmark/build.gradle.kts b/apps/flipcash/benchmark/build.gradle.kts index cac705b55..900d31e7c 100644 --- a/apps/flipcash/benchmark/build.gradle.kts +++ b/apps/flipcash/benchmark/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("com.android.test") } -val contributorsSigningConfig = ContributorsSignatory(rootProject) +val contributorsSigningConfig = ContributorsSignatory(rootDir) android { namespace = "com.flipcash.benchmark" diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt index c6e115c01..d093322f9 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt @@ -22,8 +22,12 @@ class AndroidLibraryComposeConventionPlugin : Plugin { } extensions.configure { + // Use the Isolated Projects-safe accessor for the root directory. + // `rootProject.layout` reaches into another project's model, which + // Isolated Projects forbids; `isolated.rootProject` is the blessed + // read-only view that exposes the root project directory safely. stabilityConfigurationFile.set( - rootProject.layout.projectDirectory.file("compose_compiler_config.conf") + isolated.rootProject.projectDirectory.file("compose_compiler_config.conf") ) } diff --git a/buildSrc/src/main/java/ContributorsSignatory.kt b/buildSrc/src/main/java/ContributorsSignatory.kt index d98651cf1..4f57da6c9 100644 --- a/buildSrc/src/main/java/ContributorsSignatory.kt +++ b/buildSrc/src/main/java/ContributorsSignatory.kt @@ -1,19 +1,22 @@ -import org.gradle.api.Project +import java.io.File import java.util.Properties - -class ContributorsSignatory(project: Project) { - private val signingProperties: Properties - - init { - val propertiesFile = project.file("contributors.properties") - signingProperties = Properties().apply { - load(propertiesFile.inputStream()) - } +/** + * Reads the contributor signing config from `contributors.properties` at the + * given [rootDir]. + * + * Takes the root directory as a [File] rather than a [org.gradle.api.Project] so + * callers don't reach into another project (`rootProject.file(...)`), which + * Isolated Projects forbids. Paths resolve as `Project.file` did: relative + * entries against [rootDir], absolute entries as-is. + */ +class ContributorsSignatory(rootDir: File) { + private val signingProperties: Properties = Properties().apply { + rootDir.resolve("contributors.properties").inputStream().use { load(it) } } - val keystore = project.file(signingProperties.getProperty("KEYSTORE_FILE")) + val keystore: File = rootDir.resolve(signingProperties.getProperty("KEYSTORE_FILE")) val keystorePassword: String = signingProperties.getProperty("KEYSTORE_PASS") val keyAlias: String = signingProperties.getProperty("KEY_ALIAS") val keyPassword: String = signingProperties.getProperty("KEY_PASS") -} \ No newline at end of file +}