I want to implement a plugin with optional user-provided dependencies, for example like some Gradle other plugins allow to:
dependencies {
// ...
dokkaPlugin(libs.dokka.versioning.plugin)
}
I managed to create a configuration
in the project with name myPlugin
:
val config = project.configurations.create("myPlugin")
So now, in client projects using my plugin I can do:
dependencies {
myPlugin("com.example:example:1.0.0")
}
The question is: how to apply my configuration to run some java code with classpath containing myPlugin
dependencies?
Here is some more code for context:
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create<MyPluginExtension>("myPlugin")
val config = project.configurations.create("myPlugin")
val task = project.tasks.register<MyTask>("myTask") {
// ...
}
}
}
class MyTask : DefaultTask() {
@TaskAction
fun execute() {
val config = project.configurations.getByName("myPlugin")
// How to run some java code using this config?
}
}
I understand that during compile time of my plugin these client-provided dependencies won't be available, so I won't be able to use classes from com.example:example
directly. But I still want to create some instances using reflection or get them using ServiceLoader
API.
Configuration
extends FileCollection
, so it's a wrapper around a group of files. You can resolve that configuration when you are ready and add it to a classpath.
Gradle has a built-in task JavaExec that you can use to create your own tasks executing Java programs.
So you can register a new task and configure it with all the same items you would add as if you were running a java
command from the command line:
tasks.register<JavaExec>("myRunTask") {
args("something the program needs to know")
classpath(
theJarsWithTheCompiledCodeOfMyProgram, // Build this in a separate project
myNewConfiguration // Defined in the plugin for user's use
)
mainClass.set("the.main.clazz")
}
To get your hands on theJarsWithTheCompiledCodeOfMyProgram
, you can create a additional subproject MyPluginApp
to your plugin in a multiproject project; and specify the MyPluginApp
JAR as a dependency of the project the plugin is applied in using MyPluginApp
's Maven coordinates, ensuring that the target project has access to a repository with that JAR in it (or it is in the same Gradle build).
If that all seems like too much, alternatively you can run everything within the plugin by resolving your new configuration and using the resulting files to create a new URLClassLoader instance and load what you need from them.
Such code would be triggered from the @TaskAction
method of task you define.