-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
incremental compilation fixes #836
Conversation
215c6ef
to
e91d89b
Compare
compiler-api/src/main/java/com/squareup/anvil/compiler/api/GeneratedFile.kt
Show resolved
Hide resolved
0e31737
to
a8662a7
Compare
dce7f22
to
f0a7e6d
Compare
f0a7e6d
to
243b078
Compare
build-logic/conventions/src/main/kotlin/com/squareup/anvil/conventions/BasePlugin.kt
Show resolved
Hide resolved
compiler-api/src/main/java/com/squareup/anvil/compiler/api/AnvilContext.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed on a call with @RBusarow
243b078
to
756ab5a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partial review, still working my way through most of the tests
compiler-api/src/main/java/com/squareup/anvil/compiler/api/GeneratedFileWithSources.kt
Outdated
Show resolved
Hide resolved
...ls/src/testFixtures/java/com/squareup/anvil/compiler/internal/testing/SimpleCodeGenerator.kt
Outdated
Show resolved
Hide resolved
...ls/src/testFixtures/java/com/squareup/anvil/compiler/internal/testing/SimpleCodeGenerator.kt
Outdated
Show resolved
Hide resolved
...ls/src/testFixtures/java/com/squareup/anvil/compiler/internal/testing/SimpleCodeGenerator.kt
Outdated
Show resolved
Hide resolved
compiler/src/main/java/com/squareup/anvil/compiler/codegen/CodeGenerationExtension.kt
Outdated
Show resolved
Hide resolved
compiler/src/main/java/com/squareup/anvil/compiler/codegen/incremental/GeneratedFileCache.kt
Outdated
Show resolved
Hide resolved
compiler/src/main/java/com/squareup/anvil/compiler/codegen/incremental/GeneratedFileCache.kt
Show resolved
Hide resolved
compiler/src/test/java/com/squareup/anvil/compiler/codegen/CodeGenerationExtensionTest.kt
Show resolved
Hide resolved
gradle-plugin/src/gradleTest/java/com/squareup/anvil/plugin/LifecycleTest.kt
Show resolved
Hide resolved
* - Generated code is cached in a way that Gradle understands, | ||
* and will be restored from cache along with other build artifacts. | ||
* | ||
* By default this feature is enabled. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking maybe we should default this to disabled first and have it be opt-in while we get feedback on it, verify no major issues pop up, and also do benchmarking for performance impact. Then we can switch it to enabled by default in a follow-up release
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's my thought as well, except that while testing against a snapshot or otherwise-special build, it's more convenient to just have it enabled. So I was thinking leave it enabled by default up until release, then disable it by default, then enable it in a follow-up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's my thought as well, except that while testing against a snapshot or otherwise-special build, it's more convenient to just have it enabled. So I was thinking leave it enabled by default up until release, then disable it by default, then enable it in a follow-up.
It would be easy to get released in the wrong state that way though, and just creates additional work along the way. Shouldn't enabling for the snapshot be really easy now by just setting it in your gradle.properties file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding on to the previous comment, I think changing to false now is also preferred so that we're testing a snapshot as close to release as possible which lets us verify the expected default behavior etc
756ab5a
to
89df450
Compare
...ler/src/test/java/com/squareup/anvil/compiler/codegen/incremental/FileCacheOperationsTest.kt
Outdated
Show resolved
Hide resolved
gradle-plugin/src/gradleTest/java/com/squareup/anvil/plugin/IncrementalTest.kt
Outdated
Show resolved
Hide resolved
gradle-plugin/src/gradleTest/java/com/squareup/anvil/plugin/IncrementalTest.kt
Show resolved
Hide resolved
gradle-plugin/src/gradleTest/java/com/squareup/anvil/plugin/IncrementalTest.kt
Outdated
Show resolved
Hide resolved
gradle-plugin/src/gradleTest/java/com/squareup/anvil/plugin/IncrementalTest.kt
Outdated
Show resolved
Hide resolved
gradle-plugin/src/gradleTest/java/com/squareup/anvil/plugin/IncrementalTest.kt
Outdated
Show resolved
Hide resolved
89df450
to
22e4f97
Compare
…er generated context: #836 (comment)
…er generated context: #836 (comment)
e0c01fb
to
29da0d0
Compare
gradle-plugin/src/gradleTest/java/com/squareup/anvil/plugin/LifecycleTest.kt
Outdated
Show resolved
Hide resolved
gradle-plugin/src/gradleTest/java/com/squareup/anvil/plugin/IncrementalTest.kt
Outdated
Show resolved
Hide resolved
compiler/src/main/java/com/squareup/anvil/compiler/codegen/incremental/GeneratedFileCache.kt
Outdated
Show resolved
Hide resolved
is RelativeFile -> { | ||
if (sourceFile == RelativeFile.ANY) return true | ||
val currentMd5 = sourceFile.md5() | ||
val previousMd5 = filesToMd5.put(sourceFile, currentMd5) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be using get()
right? Right now it has a side effect of modifying the tracked MD5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is intentional, but it's tricky.
This function must always calculate the file's current md5 in order to do the comparison. As such, we might as well cache it. The filesToMd5.put(...)
returns the previously-stored value or null, so we still get to do the same comparison -- and now we get the cache update for free.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 that makes sense for optimizing. I don't remember if there's already a test for this, but if not it's probably worth adding one to make it explicit that this behavior is intentional. I would have assumed it was a bug and changed it to get
if I was reading this sometime in the future since the method name hasChanged
suggests it's only checking things
* - Generated code is cached in a way that Gradle understands, | ||
* and will be restored from cache along with other build artifacts. | ||
* | ||
* By default this feature is enabled. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding on to the previous comment, I think changing to false now is also preferred so that we're testing a snapshot as close to release as possible which lets us verify the expected default behavior etc
2ba5838
to
af5cf9c
Compare
af5cf9c
to
d7ca03d
Compare
I'm not 100% sure if it's related to this pull request, but it's very likely. I've used the latest beta with
|
We start off with new feature toggle:
trackSourceFiles
.It's present in the Gradle DSL and as a Gradle property.
The majority of changes are behind that toggle, but not all.
Changes that ignore
trackSourceFiles
GeneratedFileWithSources
replacesGeneratedFile
In order to support incremental compilation, we need to track what source files resulted in what generated files, then aggregate that data in
CodeGenerationExtension
. The existingGeneratedFile
type is perfectly situated for this, but I wanted to make it explicit whether the code being generated is going to be incremental or not, so I extracted an interface from it and introduced a new type.If
trackSourceFiles
is enabled but the consuming project is using a custom generator that returns the deprecatedGeneratedFile
, the build will fail.It is possible to return a
GeneratedFileWithSources
with an empty list of source files. Anvil will then treat the generated file as if it was generated from all source files in the source set, and it will be invalidated by any change.trackSourceFiles == true
trackSourceFiles == false
GeneratedFile
GeneratedFileWithSources
GeneratedFileWithSources
with an empty sources list
build/anvil/src-gen-<variant>
has movedWe now write a cache file as well, and need to put it somewhere. The new structure is pretty comparable to what KSP and KGP do.
Prior to these changes, the generated output's folder structure resembled this:
Now, the
src-gen-main
directory is gone and we have this:New Incremental and Caching Behavior
Tracking Anvil output in Gradle
AnvilPlugin
will add the generated source code and cache binary file asTaskOutputs
of their correspondingcompileKotlin
task:This means that Gradle will handle cache/restore logic on its own when it's a fresh build, when it's from a (Gradle) build cache, or when the task is already up to date.
This is not enough to handle incremental builds, like when a single source file is changed or if a source file is deleted.
File hashing
The files that the Kotlin compiler gives us in
CodeGenerationExtension.onAnalysisCompleted
aren't only the changed files. There is no built-in way for us to compare those inputs to the inputs of the last invocation ofonAnalysisCompleted
.We now create an MD5 hash of every file that goes through
CodeGenerationExtension
, whether it's generated or human-written. These hashes are stored inbuild/anvil/<variant name>/caches/generated-file-cache.bin
, which is written to disk after every invocation ofonAnalysisCompleted
.Tracking source <--> generated relationships
Thanks to GeneratedFileWithSources, we know exactly which source files resulted in which generated files. These relationships are also stored in
build/anvil/<variant name>/caches/generated-file-cache.bin
.Deleting out-of-date outputs
A file is considered to be changed if it is in the input files and its current MD5 hash is different from its cached hash, or if the cache doesn't contain a hash for that file.
A file is considered to be deleted if it's present in the cache but not on disk, AND it is not tracked as a generated file in the cache.
For every changed or deleted file, we delete all downstream generated files recursively. For example, in the above diagram, if
Source_2.kt
is changed, we deletegen2.kt
,gen3.kt
,gen5.kt
, andgen6.kt
.Restoring missing outputs
Because we include the generated files as
TaskOutputs
of thecompileKotlin
task, Gradle will delete those files any time it re-runs the task. This would break incremental builds, where we may only receive a subset of the source files, and therefore only re-generate a subset of the output.Before we generate any new code, we restore any missing generated files from the
generated-file-cache.bin
cache.TODO
trackSourceFiles