As I understand it is not required to have
build.gradle
in root project?
That’s correct, a project can exist with an absent build script, it will then just have the defaults, or things injected into the project configuration from outside (which then would be bad practice )
or even not good practice
Not at all.
It would be extremely bad practice if you have one and in side use subprojects { ... }
or allproject { ... }
or project(...) { ... }
or any other means of doing cross-project configuration.
But just having a root project build script is fine.
And also doing things in there can be fine, like doing aggregate reports from the other projects or similar.
It is also find to have the root project as normal project and not have subprojects at all.
All depends on the project at hand the current needs.
When plugin is needed in both subprojects is it ok then to apply plugins to the subprojects directly
For most plugins this is perfectly fine.
Though I would recommend using a version catalog to centralize version declarations.
And if you need more logic on multiple projects, you should consider to use convention plugins, for example implemented as precompiled script plugins in buildSrc
or and included build.
or should it be declared in the root project first (maybe with apply:false).
This sometimes might be necessary to overcome class loader problems.
For example if you do like said above, the plugin ends up in two different class loaders and so the classes are effectively not the same.
This can sometimes make problems with some plugins or usages.
In those cases, it might be necessary to drag up the plugin to a common class loader, for example by declaring it with apply false
in the root build script or by declaring it as runtimeOnly
dependency in buildSrc
.
But I would not do that proactively, but only when really necessary, otherwise you only add unnecessary clutter.
only one of the subprojects the build (refresh, etc…) processes fail with
In your case, the problem is indeed the Artifactory plugin, that is greatly misbehaving.
If you only apply it to a subproject, it applies itself also to the root project unasked which is very bad practice for a plugin.
So in your case applying the plugin to :lib
auto-applies it to the root project.
Then (not meaning order here, order is not guaranteed) applying to :webapp
also auto-applies it to the root project.
As long as the class paths of both build scripts are identical, because you apply the same plugins (i.e. before you add the new plugin or after you applied it to both project) the class loader is reused for both projects and so the classes of the Artifactory plugin are the same (unlike to what I said above, this is a special case).
So you apply the same plugin class a second time to the root project and that is fine with Gradle, as the second time will just be a no-op.
As soon as you make the class paths of the two build scripts different by only apply a plugin to one of them, you get different class loaders for their classpaths. That means the second application to the root project applies an effectively different plugin class, so it is indeed applied again.
It then searches for its own extensions by type and tries to register it, as it didn’t find it, as the classes are different ones and then fails as an extension with that name already exists.
By applying the new plugin to both build scripts you align the classpaths and thus the class loader is reused again and the double-application to the root project is prevented again.
By declaring the plugin in the root project build script with apply false
you do not apply it, but add it to the classpath of the root project build script. The class loader for the root project is a parent class loader of the class loaders for the subprojects. And as in Java world a class loader has to ask its parent calss loader for an asked class before serving it itself if possible, this alos mitigates the problem, as then both projects use the Artifactory classses from the root project class loader and there is not discrepancy.
So yes, the Artifactory plugin is one of those cases, where you should indeed declare it on the root project.
But even more, as the Aritfactory plugin evilly applies itself to the root project anyway, you can right away omit the apply false
and just apply it explicitly.
Or is this an artifactory plugin issue?
Exactly.