skip to content

How do you parameterize a TransformAction, and how should parameter properties be annotated so the transform caches correctly?

level: seniorimportance: should knowfreq 22%

answer

  1. interface extends TransformParameters
  2. managed Property/ConfigurableFileCollection
  3. @Input / @InputFiles + @PathSensitive
  4. params feed the cache key
  5. TransformParameters.None -> getParameters() throws

basics

~10 s

Define an interface extending TransformParameters with Property/ConfigurableFileCollection fields, annotate them like task inputs (@Input, @InputFiles + @PathSensitive), and reference them via getParameters(). Use TransformParameters.None when there's nothing to configure.

solid answer

~40 s

Configuration that the action needs at runtime goes into a `Parameters` type — an interface extending `TransformParameters` with managed properties (`Property<T>`, `ConfigurableFileCollection`, etc.). You access them inside `transform()` via `parameters` (the `getParameters()` accessor). Crucially, parameter properties are **part of the cache key**, so they must carry the same normalization annotations as task inputs: - `@get:Input` for simple value properties. - `@get:InputFiles` (or `@get:InputFile`) plus a `@get:PathSensitive(...)` and often `@get:Classpath`/`@get:Normalize` for file inputs. If no configuration is needed, type the action as `TransformAction<TransformParameters.None>` and never call `getParameters()`. You wire actual values when registering the transform with `registerTransform(...) { parameters { ... } }`. Correct annotations matter: a missing `@PathSensitive` can make the cache key absolute-path-dependent and silently kill cache hits across machines.

code

kotlin · 14 lines
kotlin
interface Params : TransformParameters {
    @get:Input
    val minify: Property<Boolean>
}

abstract class Shrink : TransformAction<Params> {
    @get:InputArtifact
    abstract val inputArtifact: Provider<FileSystemLocation>
    override fun transform(outputs: TransformOutputs) {
        val out = outputs.file("shrunk-" + inputArtifact.get().asFile.name)
        if (parameters.minify.get()) shrink(inputArtifact.get().asFile, out)
        else inputArtifact.get().asFile.copyTo(out)
    }
}

go deeper

for a junior

Knows a transform can have parameters and that None means no config.

for a middle

Can declare a TransformParameters interface with managed Property fields and read them via parameters.

for a senior

Annotates each parameter correctly for normalization and explains how params feed the cache key.

for a principal

Audits parameter annotations across a plugin's transforms to guarantee cross-machine cache hits and minimal key bloat.

## Why parameters exist A transform often needs configuration: a tool version, a flag, an auxiliary classpath. That configuration is shared across all invocations of the transform, but distinct configurations must produce distinct cache entries. Gradle models this with a typed `Parameters` object. ## Declaring the Parameters type ```kotlin interface Params : TransformParameters { @get:Input val toolVersion: Property<String> @get:InputFiles @get:PathSensitive(PathSensitivity.NONE) val auxClasspath: ConfigurableFileCollection } abstract class Instrument : TransformAction<Params> { @get:InputArtifact abstract val inputArtifact: Provider<FileSystemLocation> override fun transform(outputs: TransformOutputs) { val version = parameters.toolVersion.get() // ... } } ``` The interface uses Gradle's *managed properties* — abstract getters of `Property`, `ListProperty`, `ConfigurableFileCollection`, etc. Gradle generates the implementation; you never write a backing field. ## Annotations = cache correctness Every parameter property feeds the transform's cache key, exactly like task inputs feed a task's up-to-date check. So you annotate them the same way: - `@Input` — value-typed inputs (strings, numbers, enums, booleans). - `@InputFiles` / `@InputFile` + `@PathSensitive(...)` — file inputs, with the path-sensitivity that's correct for your use. `@Classpath` / `@CompileClasspath` for classpath-like file sets so order/normalization is handled. - `@Nested` — for nested annotated objects. Getting these wrong has real consequences. Omit `@PathSensitive` and the key may depend on absolute paths, so a teammate or CI with a different checkout path gets a cache miss. Mark something `@Input` that's actually a large file and you bloat the key. ## Accessing values Inside `transform()`, call `parameters` (Kotlin) / `getParameters()` (Java) to read the resolved values. They are already finalized — you just `.get()` the providers. ## The None case When the action needs no configuration: ```kotlin abstract class Unzip : TransformAction<TransformParameters.None> { @get:InputArtifact abstract val inputArtifact: Provider<FileSystemLocation> override fun transform(outputs: TransformOutputs) { /* ... */ } } ``` `TransformParameters.None` is a marker that signals "no parameters"; calling `getParameters()` then throws. ## Wiring values Values are bound where the transform is registered (the sibling 'registering' concern), e.g. `parameters { toolVersion.set("1.2") }`. The action class only *declares* the shape; registration supplies the data.

  • What breaks if you forget @PathSensitive on a file-typed parameter?
    The cache key can become sensitive to absolute paths, so identical content checked out at different paths (CI vs local) yields cache misses and re-runs the transform unnecessarily.
  • How do you express a transform that takes no configuration?
    Type it as TransformAction<TransformParameters.None> and never call getParameters() — doing so would throw.
  • Where are the actual parameter values set?
    At registration time via registerTransform(...) { parameters { ... } }; the action class only declares the property shapes.

saying these in an interview costs you the question

  • Annotating file parameters with @Input instead of @InputFiles + @PathSensitive.
  • Calling getParameters() on a TransformParameters.None action.
  • Adding a backing field to the Parameters interface instead of leaving getters abstract (managed property).

context