Search code examples
gradlegradle-plugin

Gradle plugin-specific dependencies


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.


Solution

  • 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.

    Running as a separate Java program

    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).

    Within the plugin

    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.