Skip to content
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

fix incremental behavior when the compilation target has IR merging but no KAPT stub generation #1024

Merged
merged 2 commits into from
Jul 12, 2024

Conversation

RBusarow
Copy link
Collaborator

@RBusarow RBusarow commented Jul 9, 2024

KGP's "classpath snapshot" incremental logic uses a previous build's .class files to track references and invalidate files. Assume there are two projects: :lib1 and :lib2. :lib2 depends on :lib1. If a change is made to an existing type in :lib1 since the last successful build, KGP will inspect the snapshot of :lib2's class files to see if it references anything in :lib1. If :lib2 didn't already reference :lib1, then the :lib2:compileKotlin task will be treated as UP_TO_DATE and will not execute again.

Anvil's module and interface merging logic only changes the IR symbols. This means that all merged references are added to the resultant binary, but not to class files. Those changes are invisible to the classpath snapshots. This is why we disable incremental compilation in KAPT stub generation tasks, but it's also a problem in non-KAPT modules that are using other merging annotations.

The fix

If a compilation merges any interfaces or modules, a file is created with their names. The AnvilPlugin checks for the existence of this file during task configuration, and disables incremental compilation for that task if it's present. The file won't exist on a fresh build, regardless of whether there will be merging, but that's okay because the build wouldn't be incremental anyway.

@RBusarow RBusarow force-pushed the rick/dependency-jar-race branch 2 times, most recently from 50311ae to fef828f Compare July 9, 2024 19:35
@RBusarow RBusarow requested a review from JoelWilcox July 9, 2024 19:35
@RBusarow RBusarow marked this pull request as ready for review July 9, 2024 19:35
…ut no KAPT stub generation

KGP's "classpath snapshot" incremental logic uses a previous build's `.class` files to track references and invalidate files.  Assume there are two projects: `:lib1` and `:lib2`.  `:lib2` depends on `:lib1`.  If a change is made to an existing type in `:lib1` since the last successful build, KGP will inspect the snapshot of `:lib2`'s class files to see if it references anything in `:lib1`.  If `:lib2` didn't already reference `:lib1`, then the `:lib2:compileKotlin` task will be treated as `UP_TO_DATE` and will not execute again.

Anvil's module and interface merging logic only changes the IR symbols.  This means that all merged references are added to the resultant binary, but not to class files.  Those changes are invisible to the classpath snapshots.  This is why we disable incremental compilation in KAPT stub generation tasks, but it's also a problem in non-KAPT modules that are using other merging annotations.

### The fix

If a compilation merges any interfaces or modules, a file is created with their names.  The `AnvilPlugin` checks for the existence of this file during task configuration, and disables incremental compilation for that task if it's present.  The file won't exist on a fresh build, regardless of whether there will be merging, but that's okay because the build wouldn't be incremental anyway.
@RBusarow RBusarow force-pushed the rick/dependency-jar-race branch from fef828f to c274d53 Compare July 9, 2024 19:36
Comment on lines +90 to +93
PluginOption(
pluginId = anvilCommandLineProcessor.pluginId,
optionName = "track-source-files",
optionValue = (trackSourceFiles && mode is Embedded).toString(),
Copy link
Member

Choose a reason for hiding this comment

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

Why not keep this in the Embedded block below since this is Embedded only?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It isn't technically Embedded only -- it's just only true for Embedded mode. It's now accessed for the IrContributionMerger parameters and that technically isn't exclusive to Embedded, so the argument must always be defined.

) {
stubsTaskProvider.configure { stubsTask ->
stubsTaskProvider.configure { stubsTask ->
if (!mergingIsDisabled()) {
Copy link
Member

Choose a reason for hiding this comment

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

Is there any downside (or benefit) to moving this check into the config block instead of avoiding the config block entirely?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

When the check happens outside the config block, it reads the variant's settings eagerly, as soon as the compilation is configured. When the check happens inside the stub gen task's config block, it has a better chance of catching any late changes made by a convention plugin or something.


if (variant.variantFilter.trackSourceFiles && !mergingIsDisabled()) {
if (task is AbstractKotlinCompile<*>) {
// The `ir-merges.txt` file indicates that the last compilation resulted in adding module
Copy link
Member

Choose a reason for hiding this comment

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

This comment is great ❤️

@RBusarow RBusarow enabled auto-merge (rebase) July 12, 2024 22:45
@RBusarow RBusarow merged commit fc360e5 into main Jul 12, 2024
17 checks passed
@RBusarow RBusarow deleted the rick/dependency-jar-race branch July 12, 2024 22:59
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.

2 participants