allprojects {
plugins.with {
withType(JavaPlugin) {
configurations.all { conf ->
if (conf.canBeResolved && !(conf.name in excludedConfigurations)) {
add(conf.name, enforcedPlatform(project(":project-dependencies")))
That did no longer work in gradle 8, because compileClasspath
is resolvable (but you can no longer add dependencies to it) but implementation
is not. Clearly I misunderstood what resolvable means.
I now added the platform explicitly to 29 configurations and it seems to work. (Well it doesn’t, actually, but that seems to be caused by a plugin that does not support gradle 8).
My question still stands:
What is the best way to mimic the spring-dependency-management plugin and add a platform dependency to all configurations that need it?
Trying to come up with an MVE was (of course) providing more insight.
Always part of the hope when asking for one
because the Spring plugin was impacting the build performance massively
And because even the maintainers of the plugin tell not to use it but instead use Gradle built-in functionality instead.
For some reason beyond me, the stackoverflow community bot deleted my question.
Here is explained why: The Community user deleted my question! What gives? - Help Center - Stack Overflow
You have now a second undelete vote, but you need a third one to get the question restored.
So the solution of “clever” me back then
Some immediate remarks:
allprojects { ... }
and subprojects { ... }
are evil and should be avoided, you should strongly consider using convention plugins instead.
the JavaDoc of project.plugins
(actually project.getPlugins()
) tells you not to use it, you should use pluginManager.withPlugin("java") { ... }
instead, or maybe even pluginManager.withPlugin("java-base") { ... }
.
Clearly I misunderstood what resolvable means.
Resolvable means it can be resolved.
Besides legacy configurations that can be consumed and resolved, there are three types.
Configurations that are just buckets for declaring dependencies like implementation
, api
, compileOnly
, …
Configurations that are meant to be consumed from downstream projects like apiElements
, runtimeElements
, …
And configurations that are meant for local resolution like compileClasspath
, runtimeClasspath
, …
Not all plugins follow this strategy, but at least the Gradle built-in configurations should follow this quite consistently.
I now added the platform explicitly to 29 configurations and it seems to work.
… but … why?
Shouldn’t it be enough to e. g. declare it on implementation
?
I think this way it should work on all necessary configurations.
compileClasspath
, runtimeClasspath
, testCompileClasspath
, testRuntimeClasspath
, …
Thanks a lot for the answer. I’ll have a look into our usage of allprojects/subprojects and project.plugins.
… but … why?
Unfortunately, there are a lot of configurations added by plugins or even by our own build (for example for JavaExec tasks). Some of these need the platform, but some break (with strange errors) when the platform gets added to them.
Here’s a list of the configurations; I currently use:
acmeBeanGenerator, annotationProcessor, bootArchives, codenarc, compileOnly, acmeWar, coverageDataElementsForTest, developmentOnly, acmePeStandard, filesystemResources, implementation, jaxb, loader, mainPmdAuxClasspath, mainSourceElements, overlay, pmdAux, productionRuntimeClasspath, providedCompile, providedRuntime, runtimeOnly, tar, testAnnotationProcessor, testCompileOnly, testImplementation, testPmdAuxClasspath, testResultsElementsForTest, testRuntimeOnly, war
Probably this list can be reduced if I figure out which configurations extend which others.
Also some of these configurations probably do not really need the platform, but it is very time consuming to figure that out (e.g. codenarc
needs it, but pmd
breaks the build if the platform is added to it).
Just to illustrate the point: Here are a couple of configurations that cause errors, when I add the platform to is:
checkstyle: ClassNotFoundException: com.puppycrawl.tools.checkstyle.ant.CheckstyleAntTask
jacocoAgent: IllegalStateException: Expected configuration ‘:claims:jacocoAgent’ to contain exactly one file, however, it contains no files.
pmd: ClassNotFoundException: net.sourceforge.pmd.PMD
Also, canBeResolved
and canBeConsumed
are no help in deciding whether to add the platform:
implementation
is neither consumable nor resolvable, but codenarc
is both (and also really needs the platform).
Also, canBeResolved
and canBeConsumed
are no help in deciding whether to add the platform:
implementation
is neither consumable nor resolvable, but codenarc
is both (and also really needs the platform).
As I said, the ones that are neither consumable nor resolvable are usually the ones where dependencies are declared on, ones that have one of the two set are the ones that just extend one of the former and are used for resolving or providing for consumption, and one where both are true
are legacy. That the codenarc
configuration has both true
is probably just for backwards compatibility I guess, but I don’t know.
As I said, the ones that are neither consumable nor resolvable are usually the ones where dependencies are declared on,
Some of own configurations, are used like this:
configurations {
acmeBeanGenerator
dependencies {
acmeBeanGenerator 'com.acme.models:acme-bean-generator'
task generateAcmeBeans(type: JavaExec) {
classpath = configurations.acmeBeanGenerator
I guess I can declare that configuration as not consumable, but it must be resolvable to be used in the classpath
for the JavaExec
, right?