skip to content

Can a custom task have multiple @TaskAction methods, and how do task actions and doFirst/doLast relate?

level: middleimportance: nice to knowfreq 30%

answer

  1. task = ordered action list
  2. multiple @TaskAction run in declaration order
  3. doFirst prepends, doLast appends
  4. order: doFirst -> actions -> doLast
  5. doFirst/doLast for ad-hoc tweaks

basics

~10 s

Yes, a task can have several @TaskAction methods; Gradle runs them in declaration order. doFirst and doLast add extra actions that run before and after the @TaskAction methods on the task's action list.

solid answer

~40 s

A custom task may declare more than one `@TaskAction` method. Gradle collects them and runs them **in declaration order** as part of the task's ordered list of actions. Most tasks use a single action method, but multiple are legal. Separately, `doFirst { }` and `doLast { }` are imperative ways to *prepend* or *append* additional actions to that same list at configuration time: `doFirst` actions run before the `@TaskAction` methods, `doLast` actions run after them. So the full execution order is: all `doFirst` actions (last-added first), then the `@TaskAction` methods in declaration order, then all `doLast` actions. `doFirst`/`doLast` are most useful for ad-hoc tweaks to existing tasks from a build script, whereas `@TaskAction` is the proper place for a custom task type's core logic.

code

kotlin · 10 lines
kotlin
abstract class TwoStep : DefaultTask() {
    @TaskAction fun first() = logger.lifecycle("step 1")
    @TaskAction fun second() = logger.lifecycle("step 2")
}

tasks.register("demo", TwoStep::class) {
    doFirst { logger.lifecycle("before") }
    doLast  { logger.lifecycle("after") }
}
// before, step 1, step 2, after

go deeper

for a junior

Know @TaskAction holds the work and doLast adds a closing action; one action method is typical.

for a middle

Explain the ordered action list, multiple @TaskAction in declaration order, and doFirst/doLast prepend/append semantics.

for a senior

Advise using @TaskAction for reusable types and reserving doFirst/doLast for ad-hoc augmentation of existing tasks.

for a principal

Set guidance discouraging doFirst/doLast logic creep in shared builds, favoring typed tasks for testability and config-cache cleanliness.

## A task is an ordered list of actions Under the hood every Gradle task holds an **ordered list of actions** (`Action<? super Task>`). When the task executes (and is not up-to-date), Gradle runs each action in order. There are three ways actions get onto that list: 1. **`@TaskAction` methods** on the task class — added in **declaration order**. 2. **`doFirst { }`** — *prepends* an action to the front of the list. 3. **`doLast { }`** — *appends* an action to the end of the list. ## Multiple @TaskAction methods This is legal: ```kotlin abstract class TwoStep : DefaultTask() { @TaskAction fun first() = logger.lifecycle("step 1") @TaskAction fun second() = logger.lifecycle("step 2") } ``` Both run, `first()` then `second()`. Idiomatically you use one action method and call private helpers from it, but the framework supports several. ## Ordering with doFirst/doLast ```kotlin tasks.register("demo", TwoStep::class) { doFirst { logger.lifecycle("before") } doLast { logger.lifecycle("after") } } ``` Output order: `before`, `step 1`, `step 2`, `after`. Note `doFirst` *prepends*, so calling it twice runs the **most recently added** `doFirst` first. `doLast` appends, so multiple `doLast`s run in the order added. ## When to use which - Use **`@TaskAction`** for the core behavior of a reusable custom task *type*. - Use **`doFirst`/`doLast`** to augment an *existing* task instance from a build script without subclassing — e.g. printing diagnostics around `compileJava`, or doing cleanup after a copy. ## Gotcha: ad-hoc tasks with no @TaskAction `tasks.register("x") { doLast { ... } }` creates a plain `DefaultTask` with only a `doLast` action and no `@TaskAction` method — perfectly valid for one-off script tasks, though a real type should use `@TaskAction`.

  • In what order do multiple @TaskAction methods run?
    In the order they are declared on the class; Gradle adds them to the task's action list top to bottom.
  • Where do doFirst and doLast actions run relative to @TaskAction methods?
    doFirst actions run before all @TaskAction methods (prepended), and doLast actions run after them (appended).

saying these in an interview costs you the question

  • Claiming a task can have only one @TaskAction method.
  • Saying doFirst runs after the @TaskAction methods.
  • Confusing doFirst/doLast (action ordering) with dependsOn/mustRunAfter (task ordering).

context