Skip to content

feat(pkl-gradle): Implement gradle configuration cache support#1500

Open
ffluk3 wants to merge 1 commit intoapple:mainfrom
ffluk3:gradle-configuration-cache
Open

feat(pkl-gradle): Implement gradle configuration cache support#1500
ffluk3 wants to merge 1 commit intoapple:mainfrom
ffluk3:gradle-configuration-cache

Conversation

@ffluk3
Copy link
Copy Markdown

@ffluk3 ffluk3 commented Apr 6, 2026

Fixes #1425

Gradle's configuration cache is one of the more impactful performance features in modern Gradle. It serializes the task graph after the first configuration phase so subsequent builds can skip it entirely. For large projects this can mean the difference between a multi-second configuration overhead and near-instant task scheduling.

The root cause preventing pkl-gradle from adopting the configuration cache was a handful of patterns the configuration cache explicitly prohibits: holding a reference to the Project object in a plugin field, accessing getProject() inside task action code at execution time, and caching CLI option objects that close over non-serializable state. These patterns work fine in a standard Gradle build, but they prevent Gradle from safely serializing the task graph to disk.

What changed

PKL Plugin rework

The most visible structural change in PklPlugin is the removal of the @LateInit private Project project field. Rather than storing the project on the plugin instance, it's now passed as a parameter through the configure call chain. This is a mechanical but pervasive refactor, since every configure* method gains a project argument and passes via property drilling.

Per-task changes

  • BasePklTask captures project.getLayout().getProjectDirectory() at configuration time into a getWorkingDir() DirectoryProperty, rather than calling getProject().getProjectDir() at execution time. CliBaseOptions is now constructed fresh each time it's needed inside a task action, which is a trade-off between the (potentially negligible) cost of construction, and caching it, which would require holding a cross-boundary reference that defeats the configuration cache.
  • EvalTask's lazily-cached Provider<CliEvaluator> is similarly replaced with a factory method, and JavaCodeGenTask and KotlinCodeGenTask leverage the output dir.
  • In ModulesTask, getTransitiveModules() changes from a ListProperty<File> to a ConfigurableFileCollection (enabling .from() wiring rather than .set()), and gains a @PathSensitive(RELATIVE) annotation so the task doesn't unnecessarily re-run when files are moved between machines but their content and relative paths are unchanged. The parsedSourceModulesCache map is also removed — it was there to avoid redundant parsing but is incompatible with the cache since it holds mutable state across invocations.

Cleanup

The logic for parsing transitive file imports out of the AnalyzeImports JSON output was previously duplicated in three places across AnalyzeImportsTask and PklPlugin, with inconsistent error handling. This is consolidated into a single public static parseTransitiveFiles(RegularFile) method on AnalyzeImportsTask, which throws with a descriptive message if parsing fails rather than silently returning an empty list and leaving dependent tasks with incomplete inputs. The two dead API methods that had accumulated on AnalyzeImportsTask are removed.

Tests

Each task type gets a new is configuration cache compatible test that runs the task twice with --configuration-cache and asserts that the first run stores a cache entry and the second reuses it. The test infrastructure for this lives in a new runTaskWithConfigurationCache helper on AbstractTest. NOTE: these tests run slightly slower than the rest of the suite because they need the forked Gradle process to execute twice to prove out configuraion cache usage.

Modern versions of Gradle support configuration caching
to prevent the gradual increase of project size to affect
the overall developer experience of Gradle builds. To
prepare the PKL project, and specificall pkl-gradle, for
configuration support, we introduce an integration test to
vet configuration cache rules, and then perform the necessary
updates to provide configuration cache support.

Fixes apple#1425
Copy link
Copy Markdown
Contributor

@HT154 HT154 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Just one little nit.

This seems reasonable to me, but I'm going to defer to @bioball's greater Gradle expertise for final approval.


private final Map<List<Object>, Pair<List<File>, List<URI>>> parsedSourceModulesCache =
new HashMap<>();
@org.gradle.api.tasks.PathSensitive(org.gradle.api.tasks.PathSensitivity.RELATIVE)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tend to prefer importing the types here instead of the fq name at the usage site.

Suggested change
@org.gradle.api.tasks.PathSensitive(org.gradle.api.tasks.PathSensitivity.RELATIVE)
@PathSensitive(PathSensitivity.RELATIVE)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support the Gradle Configuration Cache with pkl-gradle

2 participants