Closed MartinHaeusler closed 8 months ago
New insight: The idea
plugin itself only seems to operate as a top-level plugin.
For instance, if I do this in a sub-project:
plugins {
idea
}
println(idea.project)
... it prints null
(which explains the NullPointerException
).
If I do the same thing in the top-level project, I get:
org.gradle.plugins.ide.idea.model.IdeaProject_Decorated@100fc7c0
This is consistent with my other observations and explains the NullPointerException
. The questions that remain are:
This looks like a bug to me, but in the case of the example I'd try not to apply the godot plugin to the root project.
@Frontrider I didn't apply the plugin top-level (apply false
). I'm already working on a fix but currently struggling with the local test setup. Got some clues om discord. If things work out the way I want them to there will be a PR this weekend.
@MartinHaeusler thx for looking into this. If you need more help with the gradle setup for local development, feel free to ask.
I'll assign you the issue for now then.
@chippmann I've made some progress thanks to your hints, but testing the godot-kotlin-jvm gradle plugin locally turns out to be an extremely deep rabbit hole. So here's what I've found.
I'm still having a lot of issues with the local (debug) deployment of the godot-kotlin-jvm gradle plugin. The bug I set out to fix has been addressed, the errors I'm seeing now (KSP) seem completely unrelated to my changes and may be due to a local setup problem.
I'm uncertain how to proceed. Do you want me to create a pull request so someone with a proper test setup can verify the fix, even though I can't seem to create a fully working build with my local snapshot?
The godot-kotlin-jvm plugin relies on the lifecycle of the idea
plugin to carry out a task which distributes .gdignore
files to make the editor ignore certain directories (e.g. build output). To do so, the task is registered like this (setupSyncTask.kt
):
ideaExtension.project.settings {
taskTriggers {
afterSync(generateGdIgnoreFilesTask)
}
}
Unfortunately that doesn't work if the godot-kotlin-jvm plugin gets applied to a sub-module, because the ideaExtension.project
returns null
for every gradle sub-module. It only returns a non-null
value for the root project. We can work around this by doing:
rootProject.ideaExtension.project.settings {
taskTriggers {
afterSync(generateGdIgnoreFilesTask)
}
}
A few adaptions in generateGdIgnoreFilesTask.kt
were also necessary to ensure that the task accesses the folders of the project it was started on (and not the root project). Also, in configureThirdPartyPlugins
I had to ensure that the idea and idea-ext plugins are added to the root project as well:
// apply the idea plugin to the *root* project.
// this is necessary because the IntelliJ IDEA plugin is used for managing some
// of our tasks, and its capabilities are available only on the root project.
if(this.rootProject != this.project) {
this.rootProject.pluginManager.apply(IdeaPlugin::class.java)
this.rootProject.pluginManager.apply(IdeaExtPlugin::class.java)
}
That's it. With this I attempted to test my project again. I built the plugin as you suggested via:
./gradlew :tools-common:publishToMavenLocal publishToMavenLocal
This worked (after I manually created the folder home/<user>/Documents/doc/classes
which for some reason needs to exist for the task to run correctly).
In my settings.gradle.kts
file I've added your proposed configuration:
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
google()
}
resolutionStrategy.eachPlugin {
if (requested.id.id == "com.utopia-rise.godot-kotlin-jvm") {
useModule("com.utopia-rise:godot-gradle-plugin:${requested.version}")
}
}
}
This did the trick - kind of. My test project can now successfully apply the godot-kotlin-jvm plugin in a sub-module (from maven local, with my snapshot version) and run the gradle task ./gradlew generateGdIgnoreFiles
with no issues. I checked, the .gdignore
files are generated in the correct locations. This was the task which previously threw the NullPointerException
that was reported in this github issue.
However, ./gradlew build
doesn't work in my test project anymore, because it requires com.utopia-rise.godot-library-debug
at version 0.7.2-4.1.2
. The build task you've proposed (:tools-common:publishToMavenLocal publishToMavenLocal
) seems to generate a different artifact which has the version 0.7.2-4.1.2-fc2f3ea-SNAPSHOT
. To work around this (at least temporarily) I did something really dirty and renamed the folders and maven entries in my .m2
directory to make gradle believe that my locally built godot-library-debug
had the version it was looking for.
With this hack in place, I'm now stuck at this error:
> Task :client:kspKotlin FAILED
e: [ksp] java.util.NoSuchElementException: Collection contains no element matching the predicate.
at com.google.devtools.ksp.UtilsKt.createInvocationHandler$lambda$8(utils.kt:553)
at jdk.proxy4/jdk.proxy4.$Proxy28.localResPath(Unknown Source)
at godot.annotation.processor.visitor.MetadataAnnotationVisitor.visitClassDeclaration(MetadataAnnotationVisitor.kt:30)
at godot.annotation.processor.visitor.MetadataAnnotationVisitor.visitClassDeclaration(MetadataAnnotationVisitor.kt:14)
... and I'm really at my wits end here, I have no clue about KSP or why this fails. Also, I can't see how this would be caused by the changes I made. If anything, I believe that this is due to the semi-faulty local deployment I have here.
Should I create a PR anyway for testing by someone with a proper local test setup?
The godot-kotlin-jvm plugin relies on the lifecycle of the
idea
plugin to carry out a task which distributes.gdignore
files to make the editor ignore certain directories (e.g. build output). To do so, the task is registered like this (setupSyncTask.kt
):
Why does it must depend on it? That would be good to know, because it might be more feasible to just do a makedir on the build folder for example.
That example is one where nobody cares which plugin creates it, and intellij should not need it as far as I'm aware.
Why does it must depend on it? That would be good to know, because it might be more feasible to just do a makedir on the build folder for example.
That example is one where nobody cares which plugin creates it, and intellij should not need it as far as I'm aware.
Excellent question. To be honest: I don't know the exact purpose. I initially assumed that it has something to do with timing and execution order, and that the task handed to the idea plugin needs to be executed in a certain point in the lifecycle in order to work properly. But considering that it literally only creates some empty marker files, I question that decision as well. I don't know the original reasoning for it.
I can see it as "the idea plugin makes a few folders, let's do work after it is done", but that is really not needed. I don't know if it does anything you actually need other than letting you configure intellij from your build. The default compat is usually more than enough for what I need.
@chippmann what was the original goal? Does it do something with editor plugin?
The reason was/is mainly the initial project creation project creation from the wizard in the intellij plugin. After the project is created and indexed by the ide, the appropriate files are created so that even before a user builds the project, the corresponding folders are ignored and the godot editor does not show them.
But i do agree that this is not really needed. A better way would be to add the corresponding files in the project template in the idea plugin and regenerate them when the user builds the project. Then we'd have a pretty standard task dependency setup without relying on the idea sync task. But the dependency is still needed as the idea plugin is also applied to the users project so idea does recognize the generated files and provides the user with some info (like syntax highlighting, and source code navigation): https://github.com/utopia-rise/godot-kotlin-jvm/blob/fc2f3eacf779c21ce18d2161cca63e6dffdb1c91/kt/plugins/godot-gradle-plugin/src/main/kotlin/godot/gradle/projectExt/configureThirdPartyPlugins.kt#L83
@MartinHaeusler i really appreciate your work here. Thanks a lot for your time! So depending on what you want to do I'm fine with either your proposed way of just creating a PR regardless of your local build so others (like myself) can look at it, or you could directly implemented the suggested change. If you're not comfortable with or do not want to edit the idea plugin template project, or do not want to invest more time, i could take over as well. I leave that up to you :-)
Regarding your local setup, we can discuss this on discord if that's ok for you
@chippmann PR containing a minimal fix is open: https://github.com/utopia-rise/godot-kotlin-jvm/pull/549
Thanks to your efforts I've been able to build the plugin locally and also consume it within my project to verify that the fix indeed works. There are also other issues I've encountered but I'll create separate tickets for them.
@chippmann The PR contains the promised fix for the NullPonterException. In the grand scheme of things, I think generating these files in the gradle plugin at all is not really necessary, and doing so with a dependency on the idea plugin doesn't seem like the best approach. Details below.
What's the worst case? The .gdignore files are not there. This will cause the godot editor to scan a few hundred text files more. Not really dramatic (unless I'm missing something?) I think, if at all, it's enough to generate the .gdignore files once in the intellij project wizard plugin / project template. Users with small projects won't notice a big difference either way, and power users with large projects likely know about .gdignore files already and will place them manually as they see fit.
Regarding the interplay between the intellij ide and the gradle plugin, to me as an "outside observer" who's not familiar with the details, it would make sense that the intellij ide plugin fetches information from the project, but the project should not depend on intellij (or their gradle module).
So, to summarize: generate the .gdignore files in the intellij project template once, but don't touch them afterwards is what I would do. Cut any dependency from the godot-kotlin-jvm plugin towards intellij.
Just my 5 cents, please correct me if I'm wrong, still pretty new to all of this.
My build setup is a fairly standard multi-module build and looks as follows:
The root
build.gradle.kts
looks like this:... and the client
build.gradle.kts
looks like this:The other
build.gradle.kts
files are empty. Thesettings.gradle.kts
looks like this:From my point of view, this should work. Sadly, I get a
NullPointerException
when I attempt to run even basic gradle tasks (such as./gradlew projects
):Aside from all the noise, the last part is interesting:
I've had a quick look into the source code of the godot gradle plugin. It seems to attempt some sort of interaction with then IntelliJ IDEA plugin. I tried adding that plugin, but unfortunately it changed nothing.
Using the gradle plugin in a standalone setup (i.e. without sub-modules) it works just fine, but it doesn't seem to play nice with multi-module builds.