`generateAtSync` feature doesn't work in all cases
jasonsparc opened this issue · 4 comments
Steps to reproduce: create an empty project with (Update: Strikethrough because it doesn't really reproduce the issue.)java
plugin applied, set up some build configs, then sync the IDE. Now open your file explorer and notice that the build
folder does not have the generated sources.
Update: I noticed that composite builds actually reproduce the issue consistently. See my comment at #121 (comment), along with #121 (comment) (which also provides a demo repro).
Also, I believe you should be using prepareKotlinIdeaImport
, and not prepareKotlinBuildScriptModel
.
Quote regarding prepareKotlinIdeaImport
(not prepareKotlinBuildScriptModel
):
Sebastian Sellmair:
This is not documented and also has no public API. However, since I have implemented this, I can safely say: It's okay to use. 🙌
– From, https://twitter.com/Sellmair/status/1619312549140516870
I believe, both prepareKotlinIdeaImport
and prepareKotlinBuildScriptModel
are (sometimes) created in an afterEvaluate
, which thus, won't be available when your plugin is applied.
Also, I believe prepareKotlinIdeaImport
is safer, and can be created eagerly, safely!
Quote:
Alexander Likhachev:
This maybeCreate in the example breaks lazy initialization of a lazily registered task 😔
Sebastian Sellmair:
It won't force initializing any other tasks, right? Only the empty prepareKotlinIdeaImport, which is basically free?
The prepareKotlinIdeaImport
task is similar to a Gradle lifecycle task: it doesn't do anything other than to exist there so that other tasks may be hooked to it. It's thus lightweight and can be safely created eagerly (unlike prepareKotlinBuildScriptModel
which appears to be a heavyweight task).
Edit: Here's one reason why I believe prepareKotlinBuildScriptModel
is a heavyweight task: https://youtrack.jetbrains.com/issue/IDEA-332603/The-Task-Preparekotlinbuildscriptmodel-UP-TO-DATE-step-takes-a-long-time
Suggestions:
Replace the lines at,
…with,
try {
tasks.maybeCreate("prepareKotlinIdeaImport")
// The above doesn't really throw, but just in case...
} catch(_: Throwable) {
null
}?.dependsOn(extension.generateAtSync.map {
if (it) tasks.withType<BuildConfigTask>() else emptyList<Task>()
})
P.S. tasks.names
is expensive. Please, don't use it. It creates an entire copy of the set of existing task names every time it's called. In fact, checking if a task exists via the lazy tasks.named(String)
API then catching the UnknownTaskException
thrown, would be way cheaper than using task.names
– just imagine a large Kotlin Multiplatform repo with many native targets and a lot of subprojects, with lots of tasks: the lookup using task.names
would be exponential.
So I found out that :prepareKotlinBuildScriptModel
is only ever run for the root project of the root build. If your project is a composite build, then it will NOT run for the root project of each included build.
Here's an example output, from a sample extracted from a monorepo I'm currently maintaining:
Sync (console output)
Starting Gradle Daemon...
Gradle Daemon started in 4 s 560 ms
> Task :build.foundation:core:checkKotlinGradlePluginConfigurationErrors
> Task :build.foundation:core:compileKotlin UP-TO-DATE
> Task :build.foundation:core:compileJava NO-SOURCE
> Task :build.foundation:core:pluginDescriptors UP-TO-DATE
> Task :build.foundation:core:processResources UP-TO-DATE
> Task :build.foundation:core:classes UP-TO-DATE
> Task :build.foundation:core:jar UP-TO-DATE
> Task :build.foundation:foojay:checkKotlinGradlePluginConfigurationErrors
> Task :build.foundation:structure:checkKotlinGradlePluginConfigurationErrors
> Task :build.foundation:dependencies:checkKotlinGradlePluginConfigurationErrors
> Task :build.foundation:support:checkKotlinGradlePluginConfigurationErrors
> Task :build.foundation:foojay:pluginDescriptors UP-TO-DATE
> Task :build.foundation:dependencies:checksumDepsCoder UP-TO-DATE
> Task :build.foundation:structure:pluginDescriptors UP-TO-DATE
> Task :build.foundation:foojay:processResources UP-TO-DATE
> Task :build.foundation:dependencies:pluginDescriptors UP-TO-DATE
> Task :build.foundation:structure:processResources UP-TO-DATE
> Task :build.foundation:dependencies:processResources UP-TO-DATE
> Task :build.foundation:support:compileKotlin UP-TO-DATE
> Task :build.foundation:support:compileJava NO-SOURCE
> Task :build.foundation:support:processResources NO-SOURCE
> Task :build.foundation:support:classes UP-TO-DATE
> Task :build.foundation:support:jar UP-TO-DATE
> Task :build.foundation:structure:compileKotlin UP-TO-DATE
> Task :build.foundation:dependencies:compileKotlin UP-TO-DATE
> Task :build.foundation:structure:compileJava NO-SOURCE
> Task :build.foundation:structure:classes UP-TO-DATE
> Task :build.foundation:structure:jar UP-TO-DATE
> Task :build.foundation:dependencies:compileJava NO-SOURCE
> Task :build.foundation:dependencies:classes UP-TO-DATE
> Task :build.foundation:foojay:compileKotlin UP-TO-DATE
> Task :build.foundation:foojay:compileJava NO-SOURCE
> Task :build.foundation:foojay:classes UP-TO-DATE
> Task :build.foundation:foojay:jar UP-TO-DATE
> Task :build.foundation:dependencies:jar UP-TO-DATE
> Task :build.foundation:complement:checkKotlinGradlePluginConfigurationErrors
> Task :build.foundation:complement:compileKotlin UP-TO-DATE
> Task :build.foundation:complement:compileJava NO-SOURCE
> Task :build.foundation:complement:pluginDescriptors UP-TO-DATE
> Task :build.foundation:complement:processResources UP-TO-DATE
> Task :build.foundation:complement:classes UP-TO-DATE
> Task :build.foundation:complement:jar UP-TO-DATE
> Task :conventions:support:checkKotlinGradlePluginConfigurationErrors
> Task :conventions:core:checkKotlinGradlePluginConfigurationErrors
> Task :conventions:support.kotlin-gradle-plugin:checkKotlinGradlePluginConfigurationErrors
> Task :conventions:support.kotlin-gradle-plugin:pluginDescriptors UP-TO-DATE
> Task :conventions:core:pluginDescriptors UP-TO-DATE
> Task :conventions:support:compileKotlin NO-SOURCE
> Task :conventions:support:compileJava NO-SOURCE
> Task :conventions:support.kotlin-gradle-plugin:processResources NO-SOURCE
> Task :conventions:core:processResources UP-TO-DATE
> Task :conventions:support:pluginDescriptors UP-TO-DATE
> Task :conventions:support:processResources NO-SOURCE
> Task :conventions:support:classes UP-TO-DATE
> Task :conventions:support:jar UP-TO-DATE
> Task :conventions:support.kotlin-gradle-plugin:compileKotlin UP-TO-DATE
> Task :conventions:support.kotlin-gradle-plugin:compileJava NO-SOURCE
> Task :conventions:support.kotlin-gradle-plugin:classes UP-TO-DATE
> Task :conventions:support.kotlin-gradle-plugin:jar UP-TO-DATE
> Task :conventions:core:compileKotlin UP-TO-DATE
> Task :conventions:core:compileJava NO-SOURCE
> Task :conventions:core:classes UP-TO-DATE
> Task :conventions:core:jar UP-TO-DATE
> Task :conventions:commonizeNativeDistribution UP-TO-DATE
> Task :conventions:commonize UP-TO-DATE
> Task :conventions:prepareKotlinIdeaImport UP-TO-DATE
> Task :conventions:testing:commonize UP-TO-DATE
> Task :conventions:testing:prepareKotlinIdeaImport UP-TO-DATE
> Task :inclusives:commonizeNativeDistribution UP-TO-DATE
> Task :inclusives:commonize UP-TO-DATE
> Task :inclusives:prepareKotlinIdeaImport UP-TO-DATE
> Task :commonizeNativeDistribution UP-TO-DATE
> Task :commonize UP-TO-DATE
> Task :prepareKotlinIdeaImport UP-TO-DATE
> Task :sample:commonize UP-TO-DATE
> Task :inclusives:testing:commonize UP-TO-DATE
> Task :sample:prepareKotlinIdeaImport UP-TO-DATE
> Task :kokoro:internal.scoping:prepareKotlinIdeaImport UP-TO-DATE
> Task :prepareKotlinBuildScriptModel UP-TO-DATE
> Task :inclusives:testing:prepareKotlinIdeaImport UP-TO-DATE
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD SUCCESSFUL in 1m 41s
44 actionable tasks: 9 executed, 35 up-to-date
Now, notice that from the above, :prepareKotlinBuildScriptModel
is only ever run once. However, the above has included builds, which are, :build.foundation
, :build.foundation:core
, :conventions
, inclusives
, etc., each of which have their own root project, and yet, :prepareKotlinBuildScriptModel
is not run for any of them. However, :prepareKotlinIdeaImport
is run consistently for each project.
I also noticed that, :prepareKotlinIdeaImport
consistently runs every time, but ONLY if you have it already created (manually), unlike :prepareKotlinBuildScriptModel
.
Regarding my previous comment, I created a demo repo, which I noticed that, consistently reproduces the issue for composite builds.
See, https://github.com/jasonsparc/someBuildConfigWithIncludedBuild
Also, my IDE is as follows:
Android Studio Hedgehog | 2023.1.1 Patch 1
Build #AI-231.9392.1.2311.11255304, built on December 27, 2023
Runtime version: 17.0.7+0-b2043.56-10550314 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
The build script that sets up your plugin is at, https://github.com/jasonsparc/someBuildConfigWithIncludedBuild/blob/master/someIncludedBuild/build.gradle.kts
Try to also uncomment the commented code at,
…and notice that IDE syncs now auto-generate the build configs every time.
Thanks for reporting this @jasonsparc!
Actually, I didn't mention how the "generate at sync" was implemented because I consider it a hack (but it works).
To build this properly, I'd also need to develop an IntelliJ plugin that will request a task, just like prepareKotlinBuildScriptModel
does.
I'm going to try hooking on both tasks prepareKotlinIdeaImport
and/or prepareKotlinBuildScriptModel
, if they exist.
Based on what you have researched, it will fix it.
Thanks for the sample repo by the way, I was able to reproduce both issues (this and #120).
Regarding this one, I've confirmed this logic works as expected:
// generate at sync
afterEvaluate {
if (extension.generateAtSync.get()) {
tasks.maybeCreate("prepareKotlinIdeaImport").dependsOn(tasks.withType<BuildConfigTask>())
}
}
I'm going to release a new version soon.