skip to content

How do you declare a task output file using RegularFileProperty, and what does Gradle do with it?

level: middleimportance: must knowfreq 50%

answer

  1. abstract val : RegularFileProperty
  2. @get:OutputFile
  3. set(Provider) stays lazy
  4. parent dirs auto-created
  5. managed property, no constructor

basics

~10 s

Declare 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 s

A `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
kotlin
@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

for a junior

Know that RegularFileProperty + @OutputFile declares a lazy file output you set from layout.buildDirectory.file().

for a middle

Explain managed properties (abstract, no constructor), set(Provider) laziness, and auto parent-dir creation.

for a senior

Tie outputs to @CacheableTask and PathSensitivity, and reason about up-to-date/stale-output behavior.

for a principal

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.

context