How does DirectoryProperty let you navigate to nested files and directories lazily, and where would you use it?
answer
- Property<Directory>
- .file()/.dir() navigation stays lazy
- buildDirectory IS a DirectoryProperty
- @OutputDirectory
- .convention() default on extension
basics
~10 sA DirectoryProperty holds a directory location lazily. From it you call .file("child.txt") or .dir("sub") to get a lazy Provider for nested paths. Use it for @OutputDirectory or a configurable base directory in an extension.
solid answer
~40 s`DirectoryProperty` is a `Property<Directory>` — a lazy, settable handle to a directory. The key feature is *navigation*: from a `DirectoryProperty` you call `.file("name")` → `Provider<RegularFile>` and `.dir("sub")` → `Provider<Directory>`, building paths relative to whatever the property ultimately resolves to. Because each result is a Provider, the whole chain stays lazy and reconfiguration-safe: changing the base directory automatically moves everything derived from it. `layout.buildDirectory` is itself a `DirectoryProperty`, which is why `layout.buildDirectory.dir("reports")` works. You annotate it `@get:OutputDirectory` for a task output, or expose it on a plugin extension (often with a `.convention(layout.buildDirectory.dir("..."))`) so users can override the base while all nested locations follow. You set it with `.set(layout.projectDirectory.dir("config"))` or another Provider.
code
kotlin · 10 linesabstract class SiteExtension { abstract val outputBase: DirectoryProperty }
val ext = extensions.create<SiteExtension>("site")
ext.outputBase.convention(layout.buildDirectory.dir("site"))
tasks.register("writeIndex") {
val index = ext.outputBase.file("index.html") // Provider<RegularFile>
outputs.file(index)
doLast { index.get().asFile.writeText("<html/>") }
}go deeper
Know DirectoryProperty holds a folder lazily and .file()/.dir() navigate into it.
Explain that navigation stays lazy so a relocated base moves all derived paths; use @OutputDirectory.
Expose configurable base dirs on extensions with .convention() and have tasks derive children from it.
Define org conventions for where plugin outputs land, keeping them relocatable and override-friendly.
## DirectoryProperty `DirectoryProperty` is the directory analogue of `RegularFileProperty`: a `Property<Directory>` that lazily holds one folder location. `layout.buildDirectory` is the most-used instance. ## Lazy navigation The distinctive power is relative navigation that stays lazy: ```kotlin val base: DirectoryProperty = layout.buildDirectory val reports: Provider<Directory> = base.dir("reports") val index: Provider<RegularFile> = base.file("reports/index.html") val nested: Provider<RegularFile> = reports.flatMap { it.file("index.html").let { f -> provider { f } } } ``` More idiomatically, you derive children directly: - `dirProp.dir("a/b")` → `Provider<Directory>` - `dirProp.file("a/b/c.txt")` → `Provider<RegularFile>` If you reassign the base (`dirProp.set(...)` or relocate `buildDirectory`), every derived Provider resolves against the new base. This is what makes relocatable build directories and configurable extensions work. ## As a task output ```kotlin abstract class Bundle : DefaultTask() { @get:OutputDirectory abstract val outputDir: DirectoryProperty @TaskAction fun run() { val d = outputDir.get().asFile // java.io.File d.resolve("manifest.txt").writeText("ok") } } tasks.register<Bundle>("bundle") { outputDir.set(layout.buildDirectory.dir("bundle")) } ``` Gradle creates the directory, tracks it for up-to-date checks, and (with `@CacheableTask`) caches its contents. ## On an extension (configurable base) ```kotlin abstract class SiteExtension { abstract val outputBase: DirectoryProperty } // plugin apply: val ext = extensions.create<SiteExtension>("site") ext.outputBase.convention(layout.buildDirectory.dir("site")) // tasks derive children: ext.outputBase.file("index.html") ``` `.convention(...)` supplies a default that the user can override; everything navigated from `outputBase` follows the override. ## Reading `dirProp.get()` → `Directory`; `.asFile` → `java.io.File`; `.asFileTree` gives a `FileTree` for walking contents. `.getAsFileTree()` is handy when you need to enumerate files within.
- Is layout.buildDirectory a Directory or a DirectoryProperty?A DirectoryProperty — that's why you can both navigate from it (.dir/.file) and, if needed, reconfigure it with .set().
- How do you give a DirectoryProperty on an extension a default the user can still override?Call .convention(layout.buildDirectory.dir("...")). The convention applies unless the user explicitly sets the property.
saying these in an interview costs you the question
- Resolving the directory eagerly with .get().asFile at configuration time and storing the File.
- Confusing Directory (resolved handle) with DirectoryProperty (lazy settable container).