This message was deleted.
# community-support
s
This message was deleted.
v
What exactly do you need? • implement
Autocloseable
so that it's
close()
method is called before it is disposed • implement
OperationCompletionListener
so that you can use it as build event listener • register it as build event listener using
BuildEventsListenerRegistry
so that it receives events about executed tasks and thus is required until the last task is executed This way somewhere between the last task being finished and the build being finished the
close
method will be called which roughly is at the end of the build.
j
I got the first two bullets easy enough. Is the Registration part like this?
Copy code
project.gradle.sharedServices.registerIfAbsent("myBuildService", MyTask::class.java) {}
The docs talk about injecting the
BuildEventsListenerRegistry
but I haven't figured out how to do that yet.
v
No, that just registers the service itself as service. Yes, you need to inject a
BuildEventsListenerRegistry
and use that to register the build service as listener. How to do it depends on where you do it, but you basically inject it like any other built-in service you inject.
As you seem to do it from within a build script, instead of a plugin and use Kotlin DSL, it would be for example
Copy code
interface BuildEventsListenerRegistryProvider {
    @get:Inject
    val buildEventsListenerRegistry: BuildEventsListenerRegistry
}

objects.newInstance<BuildEventsListenerRegistryProvider>().buildEventsListenerRegistry...
If you are doing it from a normal plugin class or similar, just define a property that gets injected.
j
Thanks. I am doing it from within a plugin. Haven't used the Property yet so still trying to figure that out.
v
"the Property"? I'm not talking about the Gradle
Property
interface, but about a normal Kotlin class property
Copy code
abstract class FooPlugin {
    @get:Inject
    abstract val buildEventsListenerRegistry: BuildEventsListenerRegistry

    override fun apply(target: Project) {
        buildEventsListenerRegistry...
    }
}
Written from mind, so might have syntax errors
j
Thanks. Interesting that I have to have @get:Inject rather than just @Inject. Some objects you can just do @Inject for example
Copy code
@Inject
protected abstract fun getObjectFactory(): ObjectFactory?
So after I inject the buildEventsListenerRegistry,
Copy code
override fun apply(project: Project) {
    val task = project.tasks.create(PLUGIN_TASK_NAME, MyTask::class.java)
    buildEventsListenerRegistry.onTaskCompletion(task)
}
That results in this error:
Copy code
Type mismatch.
Required:
Provider<out OperationCompletionListener!>!
Found:
MyTask
where MyTask is define like so:
Copy code
abstract class MyTask : DefaultTask(), AutoCloseable, OperationCompletionListener, BuildService<BuildServiceParameters.None> {
This builds... just testing it out now (took the Provider from the BuildService Registration and gave that to the OnTaskCompletion
Copy code
override fun apply(project: Project) {
        val task = project.tasks.create(PLUGIN_TASK_NAME, MyTask::class.java)
        val provider = project.gradle.sharedServices.registerIfAbsent("myBuildService", MyTask::class.java) {}
        buildEventsListenerRegistry.onTaskCompletion(provider)
    }
}
That code builds but causes this
Copy code
org.gradle.api.tasks.TaskInstantiationException: Task of type 'com.ea.gradle.plugin.coverage.CoverageTask$Inject' has been instantiated directly which is not supported. Tasks can only be created using the Gradle API or DSL.
Seems like I might need to split the Task from the Service and Listener? So another class that is a BuildService and OperationCompletionListener and the task uses that new class? 🤔
v
Thanks.  Interesting that I have to have @get:Inject rather than just @Inject.  Some objects you can just do @Inject for example
```@Inject
protected abstract fun getObjectFactory(): ObjectFactory?```
Because that is a getter, not a property. You can write it like
Copy code
@Inject
protected abstract fun getObjectFactory(): ObjectFactory?
or imho nicer and more idiomatic as
Copy code
@get:Inject
abstract val objectFactory : ObjectFactory
Nicer especially because you can then use it directly and do not have to call the getter explicitly when using it. But still the annotations needs to be effective on the getter, hence the
get:
which will put the annotation on the generated getter in the class file.
Seems like I might need to split the Task from the Service and Listener?
Might be, never tried to combine it like that, as that is not very OO. It is cleaner to separate concerns and it could well be that such a combination is not supported, but I don't know.
So another class that is a BuildService and OperationCompletionListener and the task uses that new class?
Yes, that's the usual process. While in your case the task does not use the build service. Or at least you didn't say that, you just said you want some "build finished" behaviour.
j
FYI, I got this all working now. Couple notes in case its useful: • Any task (inherits from DefaultTask() I was unable to also make a BuildService or a OperationCompletionListener. If all you need is to run some code at the end of a build, a task is not required most likely. So I moved all the task logic I had into an object that is also a BuildService or a OperationCompletionListener and so runs its code at the end of the build. • My new object, I create when my plugin is applied (i.e. instead of creating a task at this point in the plugin apply(), I create my object) • The task I removed had an extension. I was not able to get the task extension for configuration during my objects creation. The extension was never populated by the gradle builds configuration invocation probably due to the order of operations between my code and when Gradle is applying the plugin. So for now I removed the extension and used properties till I can come back to it. • I used "project" in my tasks previously however I was not able to get that in my project via injection or parameter passing. • The BuildServiceParams, every thing in here must be serializable.
v
Regarding the next to last point, what did you use
project
for? Using
project
in tasks at runtime is also bad, especially with the upcoming configuration cache in mind where it is illegal to do so.
j
I was grabbing start params, project name, project dir, root project name and dir, getting another plugins setup (like Jacoco). Using project also makes it hard to test so would love to find better ways to not need project for any future things I do.
v
Those things should be get from the project individually and set to separate properties, this way you don't need the
Project
instance at runtime
If you scroll down a bit here: https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:requirements You reach a table that advices on how to deal with certain things
project
is usually accessed for.