Gradle lazy Exec-Task configuration

589 Views Asked by At

I'm running into a problem when configuring an Exec-Task using an Extension-Property.

Problem

The configuration of my Exec-Task relies on a String-Property that is defined in an extension. Unfortunately, the property is not set yet, when configuring the Exec-Task. This leads to an TaskCreationException:

Could not create task ':myTask'.
org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreationException: Could not create task ':myTask'.
    ...
Caused by: org.gradle.api.internal.provider.MissingValueException: Cannot query the value of extension 'myConfig' property 'command' because it has no value available.
    at org.gradle.api.internal.provider.AbstractMinimalProvider.get(AbstractMinimalProvider.java:86)

Example

abstract class ConfigExtension() {
    abstract val command: Property<String>
}

class MyGradlePlugin : Plugin<Project> {
    override fun apply(project: Project) {
        val myConfig = project.extensions.create(
            "myConfig",
            ConfigExtension::class.java
        )

        val myTask = project.tasks.register(
            "myTask",
            Exec::class.java
        ) {
            it.commandLine(myConfig.command.get())
        }
    }
}

The problem seems to be the myConfig.command.get() which circumvents the lazy evaluation.

Test

@Test fun `plugin registers task`() {
    // Create a test project and apply the plugin
    val project = ProjectBuilder.builder().build()
    project.plugins.apply("com.example.plugin")

    // Verify the result
    assertNotNull(project.tasks.findByName("myTask"))
}

Question

Is there a way to configure the commandLine-Value in a lazy manner like gradle tasks should be configured? [1] [2]

2

There are 2 best solutions below

4
On

There are jvmArgumentProviders in JavaExec and argumentProviders in Exec.
One can use them like so:

jvmArgumentProviders.add { listOf("-Dfoo.bar=${lazyFooBar()}") }

There is also another trick: arguments are evaluated lazily using toString(). So you can add an object to args list with toString() method calculated lazily to the value you need.

0
On

The Exec task's properties like executable, args, or commandLine (which sets the former two), are not yet accepting Providers.

But the values are lazily evaluated using toString() when needed. So to be more lazy, you could supply some object that lazily evaluates the extension's property in its toString() method.

But actually, this would not change anything in your case. You properly leverage task configuration avoidance.
So the extension property is only evaluated when your Exec task is configured.
So as long as nothing else in your build breaks task configuration avoidance for that task, you should be fine.

The problem is your test, which does not configure the extension, but then causes the task to be realized and configured by using findByName.

You should probably either define some convention for the extension property, so that it is not unset, or set a value in your test before querying the task.