Skip to content

GSoC 2025 - Improving Configuration Cache in Key Gradle Plugins#

This project aims to improve Gradle’s build performance by enhancing Configuration Cache support in widely-used plugins.The project will help analyze cache behavior and identify areas where multiple processes compete for shared resources, which can slow down builds.

The long-term goal is to improve developer productivity and CI reliability by making Gradle builds faster, more predictable, and easier to optimize—especially in large-scale projects.

Gradle and GSoC logos

Project Status#

  • Status: Completed
  • Milestone: Midterm Evaluation Successfully Passed , waiting for the Final Evaluation
  • Current Focus: The project is now complete. The final phase involved extending Configuration Cache support to a wider range of community plugins, successfully scaling the architecture and insights gained from the initial work. Furthermore, the process provided invaluable learnings and contributed to a substantial increase in technical expertise.

Team#

Contributor#

Mentors#

  • Rodrigo Oliveira
  • Rafael Chaves
  • Oleg Nenashev

Organization#

  • Kotlin Foundation

Project Goals#

  • Survey & Identify: To survey a list of popular Gradle community plugins and identify common patterns of Configuration Cache incompatibility.
  • Fix & Contribute: To develop and submit patches for the identified incompatibilities, collaborating with plugin maintainers to get them merged.
  • Document & Share: To document the process and share key learnings with the wider Gradle community to facilitate future compatibility work.

Final Project Deliverables#

This section lists all the deliverables that were successfully completed for the project.

### 1. Nebula Gradle Lint Plugin (gradle-lint-plugin)

  • Problem: The plugin's primary violation of Configuration Cache principles was its direct reference to the Project object and its use of project.configurations to resolve dependencies at configuration time. The direct Project reference has been successfully removed. However, refactoring the usage of project.configurations is still outstanding, as this part of the work requires a more significant redesign of the plugin's logic.
  • Approach to Fix: The core approach was to decouple tasks and services from Gradle's live, non-serializable Project object during the task's execution phase. This was achieved through a consistent, two-pattern strategy: Operate on Data Snapshots: The primary fix was to create simple, serializable data containers (ProjectInfo and ProjectTree). These classes act as a "snapshot" of the necessary data, which is safely extracted from the Project object during the configuration phase using Gradle's lazy Provider API. Services like LintService were refactored to be stateless, receiving these data snapshots as input instead of the Project object itself. Defer Access with a Supplier: For rare cases where a rule absolutely required the live Project model at execution time, the fix was to inject a Supplier. This lightweight, serializable object provides a "recipe" to get the Project object on-demand, isolating the non-cache-friendly logic and allowing the rest of the process to be cached. This strategy was applied consistently across all classes. The future work plan is to continue this pattern by creating new data projections for Project.configurations and refactoring the remaining rules incrementally.
  • Technical Write-up: A detailed blog post explaining the refactoring process for this plugin was published here: Supporting Configuration Cache - my learnings from the Nebula Lint Plugin.
  • Pull Request: #433
  • Status: ⏳ Open


2. Gradle Play Framework Plugin (play-framework-plugin)#

  • Problem: The plugin had multiple Configuration Cache violations. The PlayDistributionPlugin resolved dependencies eagerly and held direct references to the non-serializable Configuration object. Separately, the JavaScriptMinify task violated caching rules by calling getProject() during its execution phase to access file operations.
  • Approach to Fix: A two-part approach was used to address the different issues: 1. For PlayDistributionPlugin: The fix was to create a lazy data pipeline using Gradle's Provider API. A new serializable data container, ComponentIdAndFile, was introduced to safely hold artifact information. This allowed the plugin to pass dependency information without resolving the Configuration object during the configuration phase. 2. For JavaScriptMinify Task: The solution was to use dependency injection. The required FileSystemOperations service was injected directly into the task's constructor, eliminating the need to call getProject() at execution time.
  • Pull Request: #207
  • Status: ⏳ Open
  • Note: This pull request is not merged as Gradle team has decided to decommission this plugin in favor of a newer, official plugin within the Play Framework that is already Configuration Cache-compatible.

3. Liquibase Gradle Plugin (liquibase-gradle-plugin)#

  • Problem: The plugin was incompatible with the Configuration Cache because its main task, LiquibaseTask, and a helper class, ArgumentBuilder, directly accessed the non-serializable Project object during the execution phase. This was done to read extension properties, resolve classpaths, and get project directories and loggers.
  • Approach to Fix: The solution was to make the task's execution logic stateless by extracting all necessary data during the configuration phase into serializable Data Transfer Objects (DTOs). * Two new data container classes, LiquibaseInfo and ProjectInfo, were created to hold all the required data from the project and its liquibase extension. * These DTOs are populated safely at configuration time and passed into the LiquibaseTask as lazy @Input properties. * The ArgumentBuilder class was refactored to be stateless, receiving the LiquibaseInfo DTO as a method parameter instead of holding a reference to the Project.
  • Pull Request: #176
  • Status: ⏳ Open (In communication with the maintainer and answering their questions)

4. Compose Multiplatform Localize (compose-multiplatform-localize)#

  • Problem: The plugin's main task contained multiple Configuration Cache violations by accessing the Project object during the execution phase. Specifically, it used project.projectDir to resolve file paths and project.logger for logging messages.
  • Approach to Fix: A two-part fix was implemented to decouple the task from the Project model: 1. The project.projectDir access was resolved by adding a new lazy @Input property to the task. This property is populated safely during the configuration phase and then read by the task at execution time. 2. The project.logger calls were replaced by using the task's own built-in logger (getLogger()), which is the cache-compatible way to handle logging within a task.
  • Pull Requests: #12, #13
  • Status: ✅ Merged

5. OpenRewrite Gradle Plugin (rewrite-gradle-plugin)#

  • Problem: The plugin was fundamentally incompatible with the Configuration Cache. Its core classes—RewriteExtension, AbstractRewriteTask, and the parsers—were deeply coupled with the live Project model, directly accessing project state and holding non-serializable Project references.
  • Approach to Fix: The refactoring is a significant, work-in-progress architectural change focused on completely decoupling the plugin from the Project model. The strategy involved several key steps: 1. The RewriteExtension was made serializable by extracting and storing only the necessary primitive data (like the project directory and properties) during its construction. 2. A comprehensive, serializable Data Transfer Object (DTO), ProjectInfo, was created to act as a safe snapshot of all necessary project data. 3. The AbstractRewriteTask base class was refactored to consume this ProjectInfo DTO as its primary input. 4. The DelegatingProjectParser was updated to be instantiated with the ProjectInfo DTO, using reflection to transfer the DTO's data across an isolated classloader boundary.
  • Validation: A dedicated functional test (ConfigurationCacheTest) was added to prove the fix. This test runs a build twice with the configuration cache enabled, verifying that no compatibility problems are found and that the cache is successfully reused on the second run.
  • Pull Request: Draft PR #1
  • Status: 🚧 Work in Progress. This is a large and complex refactoring effort. The foundational work of introducing a DTO and decoupling the core components is underway, but more work is needed for full compatibility.

Surveyed Plugins#

This section details additional plugins that were investigated for Configuration Cache compatibility, but for which a contribution was not submitted.

6. Flyway Gradle Plugin (flyway-gradle-plugin)#

  • Problem: The plugin's abstract base task, AbstractFlywayTask, is fundamentally incompatible with the Configuration Cache. Its @TaskAction logic directly queries the live Project model at execution time to access the buildscript classloader, project extensions, and source set outputs. This problematic logic can be seen in the runTask and addClassesAndResourcesDirs methods in the source code.
  • Proposed Approach to Fix: The general approach would involve refactoring the Flyway tasks to be stateless. This would require capturing all necessary configuration (like source set outputs and dependent classpaths) at configuration time into serializable data objects (DTOs) and passing them to the tasks as lazy Providers.
  • Reason for Not Proceeding: A contribution was not pursued because the maintainers decided to close the original feature request due to a lack of community interest. As stated in the official response in GitHub Issue #3550, This stance remains the official position despite newer requests like GitHub Issue #4107.
  • Status: 🛑 Blocked

7. jsonschema2dataclass Gradle Plugin (js2d-gradle)#

  • Problem: The plugin's generation task, Js2pGenerationTask, was incompatible with the Configuration Cache. When storing the task's state, Gradle produced an error indicating a type mismatch where a resolved FileCollection was being assigned to a property expecting a lazy NamedDomainObjectProvider.
  • Outcome: Following the filed issue, the maintainer corrected the incompatibility in the plugin's codebase. The fix is now present on the main branch but has not yet been part of an official release. A follow-up comment was added to the issue requesting that a new version be published.
  • GitHub Issue: #884
  • Status:Fixed (Awaiting Release)


8. forbidden-apis Gradle Plugin#

  • Problem: When the Configuration Cache is enabled, properties configured via the top-level forbiddenApis extension are not correctly applied to the tasks. This causes the task properties to be cached with empty values, leading to an error at execution time because required inputs are missing.
  • Proposed Approach to Fix: The fix would require refactoring the plugin to use Gradle's modern lazy Property APIs. The properties in the forbiddenApis extension should be correctly wired to the corresponding properties on the CheckForbiddenApis task so that their values are resolved before the task state is cached.
  • Reason for Not Proceeding: A contribution was not pursued because the plugin maintainer has clearly stated that the plugin is no longer actively maintained and that adding Configuration Cache support is not a priority.
  • GitHub Issue: #269
  • Status: 🛑 Blocked

Collaboration and Learning#

This project was a comprehensive endeavor in improving the Gradle ecosystem. The work involved a mix of surveying popular community plugins to identify Configuration Cache incompatibilities, developing technical fixes to address them, and collaborating with plugin maintainers to have those contributions reviewed and merged. This process highlighted the importance of clear communication, persistence, and technical expertise in open-source development.

The effort to make the Gradle ecosystem fully compatible with the Configuration Cache is ongoing, and there are many opportunities for others to help. The Gradle team maintains a curated list of popular plugins that still need compatibility work. For anyone interested in continuing this effort and making a valuable contribution to the Gradle community, the following GitHub issue is the best place to start:

➡️ List of Community Plugins with Configuration Cache Issues

Post-GSoC Plans#

My involvement with the Gradle community will not end with the GSoC program, as I am committed to supporting the contributions I've made and continuing to help improve the ecosystem. This includes maintaining my merged pull requests, as well as continuing the dialogue with the maintainers of the Liquibase plugin to help get that contribution merged. Looking ahead, I will continue to explore the community-curated list of plugins for new opportunities to contribute. The GSoC program has been an incredible learning experience, and I look forward to being an active member of the Gradle open-source community for a long time to come.

References#