skip to content

What is a TransformAction in Gradle, and what are the two essential pieces you must implement when writing one?

level: middleimportance: must knowfreq 35%

answer

  1. implements TransformAction<Parameters>
  2. @InputArtifact Provider<FileSystemLocation>
  3. override transform(TransformOutputs)
  4. outputs.file / outputs.dir
  5. no output = filter out

basics

~10 s

A TransformAction converts an input artifact into one or more derived output files. You implement the transform(outputs) method and mark the input file with @InputArtifact so Gradle injects it.

solid answer

~40 s

A `TransformAction<Parameters>` is the unit of work that performs an artifact transform — it takes one input artifact and produces zero or more derived files. You implement two essential things: 1. A property annotated with `@InputArtifact` (a `Provider<FileSystemLocation>`) — Gradle injects the input file/directory here. 2. The abstract `transform(TransformOutputs outputs)` method — your logic reads the input, produces results, and registers each result via `outputs.file(...)` or `outputs.dir(...)`. If the transform needs configuration, you type it with a `Parameters` class implementing `TransformParameters` (or `TransformParameters.None`). Gradle runs the action lazily, only when a consumer requests the target attributes, and caches the result keyed by the input artifact plus parameters.

code

kotlin · 10 lines
kotlin
abstract class Unzip : TransformAction<TransformParameters.None> {
    @get:InputArtifact
    abstract val inputArtifact: Provider<FileSystemLocation>

    override fun transform(outputs: TransformOutputs) {
        val jar = inputArtifact.get().asFile
        val unzipped = outputs.dir(jar.name + "-unzipped")
        // unzip jar into `unzipped`...
    }
}

go deeper

for a junior

Knows a transform converts one artifact form to another and that you implement transform(outputs) plus an @InputArtifact input.

for a middle

Can write the class, distinguish outputs.file vs outputs.dir, and explain Parameters vs TransformParameters.None.

for a senior

Explains lazy per-artifact execution, caching by input+parameters, and the no-output filtering case.

for a principal

Frames transforms as a way to keep derivation off the task graph and shares cleanly across modules/builds via the cache.

## What problem TransformAction solves Artifact transforms let Gradle convert an artifact from one *form* into another *on the dependency-resolution path*, without you wiring an explicit task. Classic example: you have JARs on the classpath but a tool needs the classes unzipped to directories. A transform turns each `jar` into a `classes-dir` lazily and per-artifact, with caching. The transform itself is expressed as a class implementing `TransformAction<T : TransformParameters>`. ## The two essential pieces ### 1. `@InputArtifact` Gradle injects the artifact being transformed into an abstract property: ```kotlin @get:InputArtifact abstract val inputArtifact: Provider<FileSystemLocation> ``` It is a `Provider<FileSystemLocation>` — `FileSystemLocation` is `RegularFile` or `Directory`. You call `.get().asFile` to obtain the `java.io.File`. The property is abstract because Gradle generates the implementation and supplies the value. ### 2. `transform(TransformOutputs outputs)` The single abstract method you override: ```kotlin override fun transform(outputs: TransformOutputs) { val input = inputArtifact.get().asFile // ... produce derived files ... } ``` You register every output through `TransformOutputs`: - `outputs.file("name")` — declares an output regular file; you then write to the returned `File`. - `outputs.dir("name")` — declares an output directory; you create/fill it. - A special case: `outputs.file(inputArtifact.get().asFile)` selects the input itself unchanged (an identity/pass-through output). Returning **no** outputs is legal and means "filter this artifact out" — the transform contributes nothing to the resolved result. ## Parameters The generic `<Parameters>` types configuration shared across all invocations: ```kotlin interface Params : TransformParameters { @get:Input val toolVersion: Property<String> } ``` Use `TransformParameters.None` when no configuration is needed. Parameter properties carry normalization/cacheability annotations (`@Input`, `@InputFiles` + `@PathSensitive`, etc.) exactly like task inputs — they feed the cache key. ## Lazy + cached execution Transforms are not tasks: they have no name in the task graph and run only when a consumer resolves a configuration requesting the produced attributes. Results are cached by Gradle keyed on the input artifact's content plus the parameters, so an unchanged JAR is transformed once and reused across builds (and across the build cache when applicable).

  • Why is the @InputArtifact property abstract?
    Gradle generates the property's implementation at runtime and injects the value, just as it does for managed task properties; you only declare the getter.
  • What happens if transform() registers no outputs?
    The artifact is effectively filtered out — it contributes nothing to the transformed result. This is a legitimate way to drop artifacts.

saying these in an interview costs you the question

  • Saying a transform is a Task with a name in the task graph — it isn't; it runs lazily during resolution.
  • Writing files to an arbitrary location instead of paths handed back by TransformOutputs.file/dir.

context