How do you declare a task output file using RegularFileProperty, and what does Gradle do with it?
answer
- abstract val : RegularFileProperty
- @get:OutputFile
- set(Provider) stays lazy
- parent dirs auto-created
- managed property, no constructor
basics
~10 sDeclare an abstract val of type RegularFileProperty annotated @get:OutputFile. Set it to layout.buildDirectory.file(...). Gradle tracks that file for up-to-date checks and the build cache, and creates parent dirs.
solid answer
~40 sA `RegularFileProperty` is a lazy `Property<RegularFile>` specialized for a single file location. In a task you declare it as `abstract val out: RegularFileProperty` and annotate it `@get:OutputFile` (or `@get:OutputDirectory` for a `DirectoryProperty`). You wire it during configuration with `out.set(layout.buildDirectory.file("reports/r.txt"))` — note you set a *Provider*, keeping it lazy. Because it's an annotated output Property, Gradle: (1) records the file as a tracked output for incremental/up-to-date checks, (2) includes it in the build-cache key/entry on a `@CacheableTask`, (3) auto-creates parent directories before the action runs, and (4) cleans stale outputs when inputs change. At execution you read `out.get().asFile`. Gradle also instantiates the property for you via managed properties, so you never call a constructor or `objects.fileProperty()` manually for `abstract` task fields.
code
kotlin · 14 lines@CacheableTask
abstract class Hash : DefaultTask() {
@get:InputFile @get:PathSensitive(PathSensitivity.NONE)
abstract val source: RegularFileProperty
@get:OutputFile
abstract val digest: RegularFileProperty
@TaskAction
fun run() {
val bytes = source.get().asFile.readBytes()
digest.get().asFile.writeText(bytes.size.toString())
}
}go deeper
Know that RegularFileProperty + @OutputFile declares a lazy file output you set from layout.buildDirectory.file().
Explain managed properties (abstract, no constructor), set(Provider) laziness, and auto parent-dir creation.
Tie outputs to @CacheableTask and PathSensitivity, and reason about up-to-date/stale-output behavior.
Standardize task I/O property patterns across a plugin suite so caching and incrementality are correct by construction.
## RegularFileProperty in one line `RegularFileProperty` is a `Property<RegularFile>` — a lazily-evaluated, settable container holding a single file location. Its sibling for folders is `DirectoryProperty` (`Property<Directory>`). Both come from Gradle's lazy-configuration API and integrate with the task input/output model. ## Declaring it on a task With **managed properties**, you declare the field `abstract` and Gradle generates the implementation (no `objects.fileProperty()` needed): ```kotlin @CacheableTask abstract class GenerateReport : DefaultTask() { @get:Input abstract val title: Property<String> @get:OutputFile abstract val reportFile: RegularFileProperty @TaskAction fun generate() { val f = reportFile.get().asFile // RegularFile -> java.io.File f.writeText("# ${title.get()}\n") } } ``` ## Wiring it lazily ```kotlin tasks.register<GenerateReport>("report") { title.set("Build Report") reportFile.set(layout.buildDirectory.file("reports/report.md")) } ``` You pass the **Provider** from `layout.buildDirectory.file(...)`; `set(Provider)` keeps evaluation deferred to execution time. ## What Gradle does with an annotated output - **Up-to-date checks:** the file's path + content are part of the task's output snapshot; if it's missing or changed unexpectedly, the task reruns. - **Build cache:** on a `@CacheableTask`, the output file is stored/restored from the cache keyed by hashed inputs. - **Directory creation:** Gradle ensures the parent directory exists before `@TaskAction` runs, so you don't `mkdirs()` yourself. - **Stale-output cleanup:** when the task is from a managed output, Gradle can remove outdated outputs. ## Reading and converting `reportFile.get()` → `RegularFile`; `.asFile` → `java.io.File`; `.asFile.toPath()` → NIO `Path`. For directories use `dirProp.get().asFile` or navigate with `dirProp.file("child")`. ## Common annotations `@OutputFile` / `@OutputDirectory` for outputs; `@InputFile` / `@InputDirectory` (+ `@PathSensitive`) for inputs that are `RegularFileProperty`/`DirectoryProperty`. The annotation, not the type, tells Gradle the role.
- Do you need to call objects.fileProperty() for an abstract RegularFileProperty on a task?No. For abstract task fields Gradle creates the managed property instance automatically. You'd use objects.fileProperty() only for plain (non-task) classes or when not using managed properties.
- Why set a Provider into the property instead of a File?set(Provider) preserves laziness so the value resolves at execution time and follows a relocated build dir; set(File) would fix it eagerly.
- What annotation makes the output also benefit from the build cache?@CacheableTask on the task class plus @OutputFile/@OutputDirectory on the property (and @PathSensitive on inputs).
saying these in an interview costs you the question
- Manually calling mkdirs() on the output's parent — Gradle does this for annotated outputs.
- Reading the property at configuration time with .get() instead of inside the @TaskAction.
- Thinking the type (RegularFileProperty) declares input vs output — the annotation does.