Build services can only be registered if the set of applied plugins is the same on all projects
melix opened this issue · comments
Expected Behavior
Given a build service:
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
abstract class MyService implements BuildService<BuildServiceParameters.None> {
}
a plugin which registers the build service:
def myService = gradle.sharedServices.registerIfAbsent("test", MyService) {
maxParallelUsages.set(1)
}
and a task which uses the build service:
abstract class MyTask extends DefaultTask {
@Internal
abstract Property<MyService> getService()
@TaskAction
void execute() {
println(service.get())
}
}
tasks.register("hello", MyTask) {
service.set(myService)
}
then the build service should be registered in any case.
Current Behavior
Build service registration fails in multi-project builds, if the projects using the build service have a different set of plugins applied:
* What went wrong:
A problem occurred configuring project ':lib'.
> Could not create task ':lib:hello'.
> Cannot set the value of task ':lib:hello' property 'service' of type MyService using a provider of type MyService.
The error is confusing, but hints at a classloading issue: the services seem to be cached by classloader, which is by set of applied plugins. Therefore there are 2 different instances of the MyService
class.
Context
Initially reported at graalvm/native-build-tools#70
Steps to Reproduce
Using the attached project attached project, cd into consumer
then run: ./gradlew hello
Now edit lib/build.gradle
and comment out the jmh plugin. Now both projects core
and lib
have the same set of plugins. Run ./gradlew hello
again and see the build pass.
Your Environment
Build scan URL: https://scans.gradle.com/s/fjgx3mlcahdfu
JMH plugin commented out: https://scans.gradle.com/s/nj3oohuwdyx2q
Workaround is to use a Provider<Object>
on the task side.
@gradle/configuration-cache Could you take a look at this one?
@gradle/bt-configuration-cache I encountered this earlier today and used the workaround from @melix (thank you!) which worked well. Now encountering something that looks the same, but for Extension objects:
Extension of type 'AwsDevCredentialsExtension' does not exist. Currently registered extension types: [..., AwsDevCredentialsExtension, ...]
Edit:
I think in in my case it could be because the extension is attempting to be acquired in an applied script i.e. apply(from = another-script.gradle.kts)
. The extension that gets created by the plugin is attached to a different class loader than the one that is used in the applied script, which is causing the issue here. It looks like this in the logs when I change to doing project.extensions.getByName("awsDevCredentials") as AwsDevCredentialsExtension
.
class gebuild.aws.AwsDevCredentialsExtension cannot be cast to class gebuild.aws.AwsDevCredentialsExtension (gebuild.aws.AwsDevCredentialsExtension is in unnamed module of loader org.gradle.internal.classloader.VisitableURLClassLoader @30cb7b03; gebuild.aws.AwsDevCredentialsExtension is in unnamed module of loader org.gradle.internal.classloader.VisitableURLClassLoader @19c59d0e)
Edit 2:
Also affects NamedDomainObjectContainer
s.
Hi team! Do you plan to investigate this one? It is quite problematic as a "well designed" plugin can be facing this issue independently in a multi-project build, so testing wouldn't highlight the problem.
@melix if you add the plugin to the root build.gradle
with apply: false
does it workaround the issue?
i.e. in the build.gradle
for the root project (parent of core
and lib
):
plugins {
id 'my.plugin' apply false
}
Are you planning to provide a fix for this issue? Plugin developers now have to start putting in custom handling to notify users that they are "using it wrong". Or is the assumption that the check will be relaxed based on #23204 (comment)?
@bmuschko Just to be clear, the reversal of the check you mentioned only affects the failure mode - the underlying issue described in the present issue still occurs, in one case (as implemented today), at usage time, in the other, at build service registration time (which we reckon is more helpful, and which we plan to re-add in a more conservative way).
The actual issue is on our radar, but no target release at this time.
the underlying issue described in the present issue still occurs
Is there a public discussion we can follow where progress on this issue can be tracked?
@Sineaggi This ticket is that place at this time, afaik.
Ping, we're seeing more users report this issue, it's rather annoying.
We (Square/Block) are seeing this as well.
Ping, we're seeing more users report this issue, it's rather annoying.
In my case, I think it's because of having it part of start.spring.io since I only knew about it through there and it's their first dependency option.
The other workaround is to move the plugins to buildSrc
, but it's kind of complicated because basically it requires changing all the generated projects, and it's not strictly equivalent...
Another occurrence: micronaut-projects/micronaut-gradle-plugin#706
This is an extremely annoying bug.
I ran across this as well when using https://github.com/vanniktech/gradle-maven-publish-plugin in a multi-project build:
Could not create task ':gradle-bsp-server:dropRepository'.
Cannot set the value of task ':gradle-bsp-server:dropRepository' property 'buildService' of type com.vanniktech.maven.publish.sonatype.SonatypeRepositoryBuildService using a provider of type com.vanniktech.maven.publish.sonatype.SonatypeRepositoryBuildService.
It's debilitating in this case because it breaks both IDE sync and CI publishing.
Same issue here Triple-T/gradle-play-publisher#1053
Are there any known workarounds?
Same issue here Triple-T/gradle-play-publisher#1053
Are there any known workarounds?
Yeah - just read the issue, they are mentioned above.
In order to start using Gradle's config caching feature we need to move to a recent version of gradle-docker-plugin, but that does not work in multi-project setup gradle-docker-plugin/1123 due to GRADLE-17559.
Would anybody have an ETA? We would love to use config caching.
In order to start using Gradle's config caching feature we need to move to a recent version of gradle-docker-plugin, but that does not work in multi-project setup gradle-docker-plugin/1123 due to GRADLE-17559.
Would anybody have an ETA? We would love to use config caching.
Does the solution mentioned in gradle-docker-plugin work for you? bmuschko/gradle-docker-plugin#1123 (comment)
Thank you very much for getting back on this, @Sineaggi !
The docker plugin is not directly applied in our Gradle project scripts. Due to product/build size our Gradle sub-projects are using standardized scripts. E.g. any project producing docker images uses apply from: "$rootDir/gradle/docker.gradle"
. It is this docker.gradle
, which applies the docker plugin via
buildscript {
dependencies { classpath 'com.bmuschko:gradle-docker-plugin:9.3.2' }
}
...
apply plugin: com.bmuschko.gradle.docker.DockerRemoteApiPlugin
The problem for us with the above mentioned work-around is that plugins
section is not allowed in scripts. It causes Only Project and Settings build scripts can contain plugins {} blocks
.
So my understanding is that we only have three options
- restructure overall build in all our repos (not an option, as we need standardisation)
- convert
docker.gradle
into a binary plugin (unlikely that we will get funding for such refactoring (across dozens of repos) plus having a binary plugin makes change process harder.) - wait for this bug to be addressed (obviously our preferred option :-) )
Can you see any other options? or is there some veeeery rough ETA?
Update: it appears that just by adding the suggested plugins section into each of our project scripts works around the issue and still allows us to continue using our docker.gradle
script plugin. - Not perfect, but definitely acceptable.
Update: Still stuck: putting plugins section into root project is now giving me:
Cannot query the value of task ':dataflow-service:dockerBuildImage' property 'dockerClientService' because it has no value available.
during execution phase. Tried many variations.
@crkurz I am trying to work around all the Gradle Docker plugin issues by building within Docker and letting it manage the build cycle. The main issues I generally face are
- caching of dependencies (too many copies and the Gradle's inability to share a common set of deps... you need the workaround with the read-only cache)
- TestContainer testing because it's a container inside a container knowing about the container testing. It's possible but not straight forward. In which case I just delegate the tests outside and let Gradle handle that part.
@melix if you add the plugin to the root
build.gradle
withapply: false
does it workaround the issue?i.e. in the
build.gradle
for the root project (parent ofcore
andlib
):plugins { id 'my.plugin' apply false }
I can confirm that adding all my plugins to the root build.gradle
with apply: false
works around the issue in my own project.