Thank you for the detailed response. It took me a while to sort everything out, but I think I finally got it working.
Like I mentioned, before posting the question I reviewed this page in the doc that is linked in the error message:
https://docs.gradle.org/8.14.4/userguide/how_to_share_outputs_between_projects.html#variant-aware-sharing
But the samples didn’t work (I got syntax errors in IntelliJ) and anyway they didn’t look anything like what you were suggesting. Then I switched to the current (9.5.1) version of this page:
https://docs.gradle.org/current/userguide/how_to_share_outputs_between_projects.html#variant-aware-sharing
and then everything started to make more sense. So, based on your suggestion and the example in the doc this is what I came up with:
val webserverRuntimeDependencies by configurations.dependencyScope("webserverRuntimeDependencies")
val webserverRuntime by configurations.resolvable("webserverRuntime") {
extendsFrom(webserverRuntimeDependencies)
}
tasks.buildZip {
val webserverClasspath = webserverRuntime.files.map { it.name }
into("$baseDirName/webapps/myapp") {
with(tasks.war.get())
exclude { webserverClasspath.contains(it.name) }
}
// For debugging
doLast {
println(webserverClasspath)
}
}
You also mentioned to request the “typical attributes for JVM dependencies”, but I am not sure what those are, so I tried the following:
I displayed the attributes of runtimeClasspath:
$ ./gradlew :webserver:outgoingVariants --variant runtimeClasspath
> Task :webserver:outgoingVariants
--------------------------------------------------
Variant runtimeClasspath
--------------------------------------------------
Runtime classpath of source set 'main'.
...
Attributes
- org.gradle.category = library
- org.gradle.dependency.bundling = external
- org.gradle.jvm.environment = standard-jvm
- org.gradle.jvm.version = 21
- org.gradle.libraryelements = jar
- org.gradle.usage = java-runtime
...
And based on this I added these attributes:
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named(TargetJvmEnvironment.STANDARD_JVM))
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
}
But it didn’t actually make any difference whether I was requesting all these attributes, or no attributes at all. However, the problem with this approach was that it didn’t give me the correct result. (Besides, I don’t understand how does this even supposed to work, I mean, how does Gradle know what to return?)
So, here is the partial output from the above println statement when the task got executed:
[bcprov-jdk18on-1.84.jar, commons-codec-1.18.0.jar, commons-text-1.13.1.jar, commons-lang3-3.20.0.jar, jakarta.xml.bind-api-4.0.2.jar, ..., jakarta.activation-api-2.1.3.jar, ...]
and from the command: ./gradlew :webserver:dependencies --configuration runtimeClasspath
Project ‘:webserver’
runtimeClasspath - Runtime classpath of source set ‘main’.
±-- org.bouncycastle:bcprov-jdk18on:1.84
| — org.bouncycastle:bcprov-jdk18on:1.84 (c)
±-- commons-codec:commons-codec:1.18.0
±-- org.apache.commons:commons-lang3:3.20.0
±-- org.apache.commons:commons-text:1.13.1 → 1.14.0
| — org.apache.commons:commons-lang3:3.18.0 → 3.20.0
±-- jakarta.xml.bind:jakarta.xml.bind-api:4.0.2 → 4.0.4
| — jakarta.activation:jakarta.activation-api:2.1.4
You can see that jakarta.xml.bind:jakarta.xml.bind-api got upgraded to 4.0.4 and with that jakarta.activation:jakarta.activation-api got upgraded too, but these changes are not reflected in the buildZip task, so these jars didn’t get excluded.
So, based on the example in the doc, I added the following to the webserver project:
val webserverClasspath by configurations.runtimeClasspath
configurations {
consumable("webserverRuntimeClasspath") {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named("webserver-classpath"))
}
outgoing {
artifacts(provider {
webserverClasspath.files
})
}
}
}
I also added the same attribute to the above code:
val webserverRuntime by configurations.resolvable("webserverRuntime") {
extendsFrom(webserverRuntimeDependencies)
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named("webserver-classpath"))
}
}
This resolved the issue and now I am getting the right versions for the jars in the buildZip task.
I didn’t verify this yet, but the only thing I can think of why this is happening is that I am using the org.gradlex.jvm-dependency-conflict-resolution plugin to do consistent resolution across the subprojects, so the main app is providing versions to webserver. But if this is the case, then apparently the plugin code is not getting executed in the first scenario, only in the second.
In any case, does this solution look OK now?
Btw, I noticed that in 9.6.0 this whole thing will break again, because the syntax will change:
https://docs.gradle.org/9.6.0-rc-2/userguide/how_to_share_outputs_between_projects.html#variant-aware-sharing
But also excluding those by file-name is quite fragile actually so think twice if you really want to do it that way.
Maybe a better way would be to actually declare those dependencies as providedCompile right away in the buildZip containing project, so that you don’t have to exclude anything, as you only have them as compile dependencies anyway.
Yes, you’re right, but the problem is that sometimes we need the whole application and its dependencies in the war file that can be deployed on a standalone app server.
Thanks.