skip to content

What is the difference between @Input and @InputFile (and @InputFiles / @InputDirectory), and when would you use each?

level: middleimportance: must knowfreq 60%

answer

  1. value vs file
  2. @Input = serialized value
  3. @InputFile = content hash
  4. File on @Input = path only trap
  5. @InputFiles for collections/empty-allowed

basics

~10 s

@Input snapshots a scalar value (string, number, enum). @InputFile/@InputFiles/@InputDirectory track file or directory content by hashing the bytes. Use @Input for plain values, the file annotations when a File/FileCollection's content matters.

solid answer

~40 s

`@Input` is for property *values* — strings, numbers, booleans, enums, or `Serializable` objects — and Gradle records the value itself in the fingerprint. The file-typed annotations exist because passing a `File` to `@Input` would only snapshot its path string, not its content, silently breaking up-to-date checks. `@InputFile` tracks one file (content hashed); `@InputFiles` tracks a `FileCollection`/iterable; `@InputDirectory` tracks a directory tree recursively. Use `@Input` for configuration like a target version or a flag; use `@InputFile(s)`/`@InputDirectory` whenever the *bytes on disk* drive the result, e.g. a template, a classpath, or a source set. A common mistake is `@Input val template: File` — it compiles but only the path participates, so editing the file won't rerun the task.

code

kotlin · 7 lines
kotlin
abstract class RenderTask : DefaultTask() {
    @get:Input            abstract val locale: Property<String>      // value
    @get:InputFile        abstract val template: RegularFileProperty  // content
    @get:InputFiles       abstract val partials: ConfigurableFileCollection // many
    @get:InputDirectory   abstract val assets: DirectoryProperty       // tree
    @get:OutputFile       abstract val out: RegularFileProperty
}

go deeper

for a junior

State that @Input is for values and @InputFile for file content; name the file-family annotations.

for a middle

Explain the path-vs-content trap and pick the right annotation per data type, including @InputFiles for collections.

for a senior

Discuss @Classpath/@CompileClasspath specializations and how content hashing enables relocatable, cacheable builds.

for a principal

Set conventions for plugin authors so file inputs are never modeled as @Input values; back it with validation in CI.

## The fundamental split Gradle distinguishes between **values** and **files**: - A *value* input is captured by its serialized value. `@Input` is the right annotation. Examples: `Property<String> version`, `Property<Boolean> verbose`, an enum, a list of strings. - A *file* input is captured by **fingerprinting its content** (hashing). The path alone is not enough — two checkouts at different paths must look identical. Hence the dedicated file annotations. ## The file annotation family - `@InputFile` — exactly one file; its content is hashed. Type is typically `RegularFileProperty` or `File`. - `@InputFiles` — a `FileCollection` or `Iterable<File>`; each file's content contributes. Also the right choice for an empty-allowed set (an empty collection is valid, whereas a missing `@InputFile` fails validation unless `@Optional`). - `@InputDirectory` — a directory; Gradle walks it and fingerprints the tree. - `@Classpath` / `@CompileClasspath` — specializations of `@InputFiles` that apply classpath-aware normalization (order matters, jar-internal timestamps don't). Useful but a refinement of the same idea. ## The classic trap ```kotlin // WRONG: only the path string is tracked @get:Input abstract val template: Property<File> ``` Edit the template's content and the path is unchanged, so the task stays UP-TO-DATE with stale output. Fix: ```kotlin @get:InputFile abstract val template: RegularFileProperty ``` ## When to use which | Need | Annotation | |------|------------| | version string, flag, enum | `@Input` | | single template/config file content | `@InputFile` | | a set of sources / a classpath | `@InputFiles` (or `@Classpath`) | | a whole source/resource directory | `@InputDirectory` | ## Why not just hash everything? Values are cheap to snapshot directly and round-trip exactly; files need content hashing and normalization. Matching the annotation to the data type keeps fingerprints correct and fast.

  • Why does @Input val file: File silently break incrementality?
    With @Input, Gradle snapshots the property's value — for a File that is its path string. Changing the file's contents leaves the path unchanged, so the fingerprint is identical and the task stays UP-TO-DATE despite different content.
  • When would you prefer @InputFiles over @InputFile even for what is conceptually one file?
    When the file may be absent: an @InputFile must point to an existing file (or be @Optional), whereas an empty @InputFiles collection is valid, which is handy for optional sets without per-property @Optional handling.

saying these in an interview costs you the question

  • Using @Input on a File or FileCollection property.
  • Believing @InputFile tracks the path — it tracks hashed content (subject to normalization).

context