I’d like to start with a “Thank you!”, especially to @Vampire and @jendrik for their help and content.
I managed to create and publish the following setup, and I’m interested in your thoughts. Is this “idiomatic Gradle”? Do you have any ideas how things can be improved?
A project that defines a version catalog for plugins and libraries I often use. This project also uses the catalog to define a consumable platform: GitHub - C-Otto/java-platform (de.c-otto.java-platform / de.c-otto:java-platform:2023.01.29)
A project that, using the versions defined in the platform, combines some Gradle plugins and applies some opinionated configuration: GitHub - C-Otto/java-conventions (de.c-otto.java-conventions / de.c-otto:java-conventions:2023.01.29_3)
I’m rather happy with this setup and I’m looking forward to using it in my non-playground projects. However, I’m also very curious about your thoughts, especially concerning the following issues:
Configuration files like checkstyle.xml required for the built-in checkstyle plugin need to exist in the project applying the convention plugin. However, I’d like to provide this configuration as part of my conventions plugin, so that I don’t have to duplicate and maintain these files in my projects. Is this possible?
In order to apply the conventions plugin, which uses my version catalog, I have to also include the version catalog in my consuming project. This is somewhat annoying and could lead to errors, if I forget to update the version number of the imported version catalog plugin.
I didn’t really look too much at your code, sorry, but too much on the plate to do an in-depth review.
But at least what you said and what I saw mostly sound pretty idiomatic to me.
But Jendrik can probably do a better statement, he is the idiomaticness expert.
Well, actually I opened the first file and it was immediately non-idiomatic in its entireness.
The top-level build script contains a subprojects { ... } closure, so it is bad.
I usually just define group and version in the gradle.properties of the root project, this way all projects get that property automatically if they don’t overwrite it themselves.
However, I’d like to provide this configuration as part of my conventions plugin, so that I don’t have to duplicate and maintain these files in my projects. Is this possible?
In order to apply the conventions plugin, which uses my version catalog, I have to also include the version catalog in my consuming project. This is somewhat annoying and could lead to errors, if I forget to update the version number of the imported version catalog plugin.
Well, then don’t use a version catalog in that situation?
Or have a setting plugin that defines the version catalog and fails if you apply the project plugin with a different version or similar.
Or have always the same version for the convention plugin and version catalog and use one version declaration for both.
Or use the version catalog in the build of the convention plugin to generate some properties file that you then package with your plugin and use instead of relying on the version catalog being present on the consumer.
So many ways to do it differently.
Relying on a version catalog at the consumer is fine if you want the consumer be able to control / supply the versions to be used.
But when you actually want the convention plugin to dictate the versions it might not be the tool of choice.
I need to use apply(plugin = ...) to consume the conventions plugin
I don’t understand this part, sorry. Would I be able to reference these properties from within my consumer, for example to take the version of my published version catalog out of there?
And I still don’t see how I can get rid of the “apply” hack, which of your suggestions “above” do you mean in particular?
Sure, why not?
You use the version catalog to generate a properties file, then you package that file as resource, so it is in the plugin jar and thus available at runtime. Your plugin code then reads that properties file from the classpath and can use the values.
The one that gets rid of using the version catalog, at execution time because then there is no problem in applying the plugin in the plugins blocks of the generator project and can just be used normally.
In my conventions plugin buildSrc code, I used the VersionCatalog to create two files. First, a TOML copy of the catalog (sort of) which I copy to gradle/libs.versions.toml and then use for my plugin’s src/build.gradle.tks. Then, more importantly, a simple versions.properties file, which I copy into src/main/resources and then parse from within the actual plugins. This way the conventions plugins are self-contained and don’t need anything provided from the consuming project.
It’s a hack, but it works. I’m happy (for now)
I also noticed that my variant of resources.text.fromUri(javaClass.classLoader.getResource("path/to/checkstyle.xml")).asFile() causes issues in the sense that tasks like SpotBugs cannot be cached, anymore. The “exclude” file (created using said snippet) gets a new random file name in each invocation, causing the task to run again even though nothing (else) changed. I “fixed” this by copying the generated file to some hardcoded file name. Again, it’s a hack, but it works.