What is the difference between @Input and @InputFile (and @InputFiles / @InputDirectory), and when would you use each?
answer
- value vs file
- @Input = serialized value
- @InputFile = content hash
- File on @Input = path only trap
- @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 linesabstract 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
State that @Input is for values and @InputFile for file content; name the file-family annotations.
Explain the path-vs-content trap and pick the right annotation per data type, including @InputFiles for collections.
Discuss @Classpath/@CompileClasspath specializations and how content hashing enables relocatable, cacheable builds.
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).