5

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]

0

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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.

4 Comments

This does not answer the question. OP tries to set commandLine which cannot be supplied using a jvmArgumentProvider.
one can set commandLine to just binary and pass everything else via provider, don't they?
Yes, one could, or rather one should set executable instead in that case. But that was not the question. OP shows commandLine with one argument which then is treated as the executable and thus cannot be provided using jvmArgumentProviders.
then a workaround might be to call sh -c or cmd /K and pass the app as a parameter

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.