How to forward reference tasks for configuration?

Let’s say I have a project where I know eventually there will be a task with name “foo”. How do I attach a configuration block to it? For example:

println("configuring build")

println("re-configuring foo")
// What do I write here to do tasks.named("foo").configure {}?
// Note: reordering afterEvaluate of foo creation and this is not an option.

afterEvaluate {
	println("registering foo")
	tasks.register("foo") {
		println("configuring foo")
	}
}

I tried

println(tasks.findByName("foo"))
println(tasks.named("foo"))

and

afterEvaluate {
	println(tasks.findByName("foo"))
	println(tasks.named("foo"))
}

but I keep getting null from findByName and from named I get:

* What went wrong:
A problem occurred configuring root project 'taskRef'.
> Task with name 'foo' not found in root project 'taskRef'.

You can’t attach anything to a task by name if it has not yet been declared (deferred configuration doesn’t change anything here, it still has to be declared first, even if not yet realized).

To get close to this, you will need to hook into the task container lifecycle so that your code executes after the task is actually added (code before the afterEvaluate block executes after the task.register('foo') executes). Without doing this, the order of code in the file still matters (both using afterEvaluate, for example).

tasks.whenTaskAdded {
    if (it.name == 'foo') {
        // configure it here
    }
}
tasks.withType(DefaultTask) { // Nearly identical to above, but might be preferred if you can be more specific about task type
    if (it.name == 'foo') {
        // configure it here
    }
}

The only potential concern is that this code will run before the configuration added when the task is registered (i.e. println("configuring foo")).

Thank you for the help!

The only potential concern is that this code will run before the configuration added when the task is registered

I guess it depends what one wants to configure. I was only looking to add dependsOn and mustRunAfters so whenTaskAdded/withType would work, but I’ve had problems in the past when I needed to alter task configuration state after the initial configuration has been done.


Otherwise it became convoluted quick, but it works now mostly after about 10 iterations of tries:

println("configuring build")

println("registering bar")
tasks.register("bar") {
	println("configuring bar")
}

configureTaskForSureAfterInitialConfiguration(project, "bar", DefaultTask) {
	println("configuring bar after register")
}
configureTaskForSureAfterInitialConfiguration(project, "foo", DefaultTask) {
	println("configuring foo before register")
}
afterEvaluate {
	configureTaskForSureAfterInitialConfiguration(project, "foo", DefaultTask) {
		println("configuring foo in afterEvaluate before register")
	}
	configureTaskForSureAfterInitialConfiguration(project, "bar", DefaultTask) {
		println("configuring bar in afterEvaluate after register")
	}
}
afterEvaluate {
	println("registering foo")
	tasks.register("foo") {
		println("configuring foo")
	}
}
afterEvaluate {
	configureTaskForSureAfterInitialConfiguration(project, "foo", DefaultTask) {
		println("configuring foo in afterEvaluate after register")
	}
}

static void configureTaskForSureAfterInitialConfiguration(
	Project project, String name, Class taskType, Action<? super Project> configure
) {
	project.tasks.withType(taskType) {
		if (it.name == name) {
			project.afterEvaluate {
				project.tasks.named(name).configure(configure)
			}
		}
	}
}
tasks.register("dummy")
> Configure project :
configuring build
registering bar
configuring bar
configuring bar after register
registering foo
configuring foo
configuring bar in afterEvaluate after register
configuring foo before register
configuring foo in afterEvaluate before register
configuring foo in afterEvaluate after register

This works everywhere by running after the initial configuration. Even if it is called after register which doesn’t work with whenTaskAdded/withType (this part wasn’t in OP). configureTaskForSureAfterInitialConfiguration seems to be a good way of making sure the task is configured correctly, even if the implementation detail of when the task created is changed inside the plugin declaring foo.


Now, there’s only one interesting issue, foo and bar get configured even when I’m not executing them: gradle dummy :frowning: I thought that using register/named would result in lazy configuration.