Kotlin-dsl-samples: Support task selection and configuration

Created on 30 May 2016  路  5Comments  路  Source: gradle/kotlin-dsl-samples

We have a clear approach to task declaration (#9, #10), but have not yet made decisions around how to configure existing tasks that have been created or declared elsewhere, e.g. by a plugin.

In Groovy, this is done as follows:

mytask {
    // additional configuration goes here
}

Where mytask is a task already registered with the tasks container.

In Kotlin, we want:

tasks.mytask {
     ...
}

Consider aggregating all build/project specific extensions, conventions and tasks under a my namespace:

my.artifactory {
   ...
}

my.taskContributedByPlugin {
   ...
}
feature kotlin-dsl-api groovy-parity task-support duplicate

Most helpful comment

I'm not sure I know the best approach for configuring these kinds of objects, especially when retrieving existing Task objects.

At least with Gradle 3.0, this is what I have tried:

Passing Closure by using closureOf call

// Example previously created task
task("myTask")

project.tasks.findByPath("myTask").configure(closureOf<Task> {
  description = "Test configuration"
})

The closureOf results in an error like this:

> Value is null
...
Caused by: java.lang.IllegalArgumentException: Value is null
        at org.gradle.internal.metaobject.BeanDynamicObject.<init>(BeanDynamicObject.java:89)
        at org.gradle.internal.metaobject.BeanDynamicObject.<init>(BeanDynamicObject.java:80)
        at org.gradle.api.internal.DynamicObjectUtil.asDynamicObject(DynamicObjectUtil.java:30)
        at org.gradle.internal.metaobject.ConfigureDelegate.<init>(ConfigureDelegate.java:35)
        at org.gradle.util.ConfigureUtil.configureSelf(ConfigureUtil.java:136)
        at org.gradle.api.internal.AbstractTask.configure(AbstractTask.java:519)
        at Build_gradle.<init>(build.gradle.kts:100)

Which looks like it comes down from either a delegate or owner not being set.

Instantiating Closure by using closureOf call and setting delegate

You could also create with closureOf, and then set the delegate.

// Example previously created task
task("myTask")

val myTask = project.tasks.getByName("myTask")
val configureMyTask = closureOf<Task> {
  description = "My Task description"
}
configureMyTask.delegate = myTask

myTask.configure(configureMyTask)

But this results in:

> Value is null
...
Caused by: java.lang.IllegalArgumentException: Value is null
        at org.gradle.internal.metaobject.BeanDynamicObject.<init>(BeanDynamicObject.java:89)
        at org.gradle.internal.metaobject.BeanDynamicObject.<init>(BeanDynamicObject.java:80)
        at org.gradle.api.internal.DynamicObjectUtil.asDynamicObject(DynamicObjectUtil.java:30)
        at org.gradle.internal.metaobject.ConfigureDelegate.<init>(ConfigureDelegate.java:35)
        at org.gradle.util.ConfigureUtil.configureSelf(ConfigureUtil.java:136)
        at org.gradle.api.internal.AbstractTask.configure(AbstractTask.java:519)
        at Build_gradle.<init>(build.gradle.kts:111)

Insantiating KotlinClosure

Creating an instance of KotlinClosure is not too friendly either:

This code leads to an unfortunate configuration:

// Example previously created task
task("myTask")

val myTask = project.tasks.getByName("myTask")

val configureMyTask = KotlinClosure<Task, Unit>({
  description = "My Task Description"
}, owner = project, thisObject = myTask)

Results in:

IntelliJ popup message with Kotlin error

It is not too bad to create and configure a KotlinClosure, but it does seem that you have to make sure that your return value is correctly typed, liked below I could be missing something with Kotlin, still new to it):

You need to return the V type, which in this case is Unit:

val configureMyTask = KotlinClosure<Task, Any>({
  description = "My Task Description"
  Unit
}, owner = project, thisObject = myTask)

You can then use it to configure the task:

myTask.configure(configureMyTask)

Using apply/with?

// Example previously created task
task("myTask")

val myTask = project.tasks.getByName("myTask")

myTask.apply {
  description = "My Task Description"
}

This _seems_ to work, but I'm not sure if there is any Gradle-Groovy AST magic that is supposed to happen during the _Configuration_ phase. It seems like the nicest of the above options for configuring Tasks, but doesn't help everywhere.

These are just my overall experience when trying to figure this out, hopefully it helps others until this feature is completed!

All 5 comments

We should start thinking about moving tasks off the top level namespace, so that all task definition and configuration is done via the task container (using whatever API/DSL that the containers happen to provide).

Moving this to 1.0 M3, where we'll focus on polishing syntax and making things more declarative as a theme.

I'm not sure I know the best approach for configuring these kinds of objects, especially when retrieving existing Task objects.

At least with Gradle 3.0, this is what I have tried:

Passing Closure by using closureOf call

// Example previously created task
task("myTask")

project.tasks.findByPath("myTask").configure(closureOf<Task> {
  description = "Test configuration"
})

The closureOf results in an error like this:

> Value is null
...
Caused by: java.lang.IllegalArgumentException: Value is null
        at org.gradle.internal.metaobject.BeanDynamicObject.<init>(BeanDynamicObject.java:89)
        at org.gradle.internal.metaobject.BeanDynamicObject.<init>(BeanDynamicObject.java:80)
        at org.gradle.api.internal.DynamicObjectUtil.asDynamicObject(DynamicObjectUtil.java:30)
        at org.gradle.internal.metaobject.ConfigureDelegate.<init>(ConfigureDelegate.java:35)
        at org.gradle.util.ConfigureUtil.configureSelf(ConfigureUtil.java:136)
        at org.gradle.api.internal.AbstractTask.configure(AbstractTask.java:519)
        at Build_gradle.<init>(build.gradle.kts:100)

Which looks like it comes down from either a delegate or owner not being set.

Instantiating Closure by using closureOf call and setting delegate

You could also create with closureOf, and then set the delegate.

// Example previously created task
task("myTask")

val myTask = project.tasks.getByName("myTask")
val configureMyTask = closureOf<Task> {
  description = "My Task description"
}
configureMyTask.delegate = myTask

myTask.configure(configureMyTask)

But this results in:

> Value is null
...
Caused by: java.lang.IllegalArgumentException: Value is null
        at org.gradle.internal.metaobject.BeanDynamicObject.<init>(BeanDynamicObject.java:89)
        at org.gradle.internal.metaobject.BeanDynamicObject.<init>(BeanDynamicObject.java:80)
        at org.gradle.api.internal.DynamicObjectUtil.asDynamicObject(DynamicObjectUtil.java:30)
        at org.gradle.internal.metaobject.ConfigureDelegate.<init>(ConfigureDelegate.java:35)
        at org.gradle.util.ConfigureUtil.configureSelf(ConfigureUtil.java:136)
        at org.gradle.api.internal.AbstractTask.configure(AbstractTask.java:519)
        at Build_gradle.<init>(build.gradle.kts:111)

Insantiating KotlinClosure

Creating an instance of KotlinClosure is not too friendly either:

This code leads to an unfortunate configuration:

// Example previously created task
task("myTask")

val myTask = project.tasks.getByName("myTask")

val configureMyTask = KotlinClosure<Task, Unit>({
  description = "My Task Description"
}, owner = project, thisObject = myTask)

Results in:

IntelliJ popup message with Kotlin error

It is not too bad to create and configure a KotlinClosure, but it does seem that you have to make sure that your return value is correctly typed, liked below I could be missing something with Kotlin, still new to it):

You need to return the V type, which in this case is Unit:

val configureMyTask = KotlinClosure<Task, Any>({
  description = "My Task Description"
  Unit
}, owner = project, thisObject = myTask)

You can then use it to configure the task:

myTask.configure(configureMyTask)

Using apply/with?

// Example previously created task
task("myTask")

val myTask = project.tasks.getByName("myTask")

myTask.apply {
  description = "My Task Description"
}

This _seems_ to work, but I'm not sure if there is any Gradle-Groovy AST magic that is supposed to happen during the _Configuration_ phase. It seems like the nicest of the above options for configuring Tasks, but doesn't help everywhere.

These are just my overall experience when trying to figure this out, hopefully it helps others until this feature is completed!

Similar problem exists with the artifacts closure.

As of #35, it is now possible to select and configure tasks with the following patterns:

Select single task by name

Use the collection indexer.

task["jar"].dependsOn("someOtherTask")

Configure multiple tasks by name

Invoke the task container (works with any DomainObjectContainer).

tasks {
    "jar" {
         dependsOn("someOtherTask")
    }

    "clean"(Delete::class) { // specify type information so `delete` is available
         delete("someOtherDir")
    }
}

Bring a task into scope for multiple references

Declare a delegated property using the container as a delegate (works with any DomainObjectCollection).

val jar: Jar by tasks
jar.apply {
    from(sourceSets["main"].allSource)
    from(sourceSets["test"].allSource)
}

tasks["build"].dependsOn(jar)
Was this page helpful?
0 / 5 - 0 ratings