What is ProjectLayout in Gradle, and how do you use it to point at a file or directory under the build directory?
answer
- project.layout service
- buildDirectory = DirectoryProperty
- .file() / .dir() → Provider
- lazy vs new File()
- RegularFile.asFile
basics
~10 sProjectLayout (project.layout) gives lazy access to the project and build directories. You use layout.buildDirectory.file("name") or .dir("name") to point at outputs under build/ without resolving paths eagerly.
solid answer
~30 s`ProjectLayout` is a service injected into every project (accessible as `project.layout`) that exposes the project's directory structure lazily. Its two key entry points are `projectDirectory` (a `Directory`) and `buildDirectory` (a `DirectoryProperty`, default `build/`). From `buildDirectory` you call `.file("report.txt")` to get a `Provider<RegularFile>` or `.dir("reports")` to get a `Provider<Directory>`. These are lazy: the path isn't resolved until the value is queried at execution time, so they respect a relocated build dir. You wire them into task output properties (`RegularFileProperty`/`DirectoryProperty`) so Gradle can track them for up-to-date checks and caching. Prefer this over `new File(project.buildDir, ...)`, which resolves eagerly and breaks lazy configuration.
code
kotlin · 4 linesval reportFile: Provider<RegularFile> = layout.buildDirectory.file("reports/out.txt")
val reportDir: Provider<Directory> = layout.buildDirectory.dir("reports")
// resolve to java.io.File only when needed
println(reportFile.get().asFile.absolutePath)go deeper
Know layout.buildDirectory / .file() / .dir() and that they return lazy Provider locations, not Files.
Explain why the Provider form matters for up-to-date checks and relocatable build dirs.
Contrast with deprecated buildDir, and wire results into typed output properties for cache/incremental correctness.
Set conventions across plugins so all output locations go through layout, enabling reliable caching and build-dir relocation org-wide.
## What ProjectLayout is `ProjectLayout` is a Gradle service that models a project's on-disk layout *lazily*. Every project can access it as `project.layout`, and it is also injectable into tasks/plugins via `@Inject` of `ProjectLayout`. It exists so that filesystem locations participate in Gradle's **lazy configuration** (Provider/Property) model instead of being resolved to absolute `java.io.File` paths at configuration time. ## The two anchors - `layout.projectDirectory` → a `Directory` for the project root (the folder containing `build.gradle(.kts)`). - `layout.buildDirectory` → a `DirectoryProperty` representing the build output directory (default `build/`). Because it's a *Property*, it can be reconfigured (e.g. `layout.buildDirectory.set(...)`) and everything derived from it follows. ## Deriving files and directories From either anchor you navigate lazily: - `layout.buildDirectory.file("reports/out.txt")` → `Provider<RegularFile>` - `layout.buildDirectory.dir("reports")` → `Provider<Directory>` - `layout.projectDirectory.file("config/app.yml")` → `RegularFile` (eager-ish, but still a typed location) `RegularFile` and `Directory` are typed handles; call `.getAsFile()` to get a `java.io.File`, or `.getAsFile().toPath()` for NIO. Crucially, the *Provider* form defers resolution until queried. ## Why not `new File(buildDir, ...)`? Using `project.buildDir` (deprecated in modern Gradle) or `new File(...)` resolves the path during configuration, before the build dir might be relocated, and the value isn't a Provider — so Gradle can't track it as a task input/output for up-to-date checks or the build cache. ```kotlin abstract class GenerateReport : DefaultTask() { @get:OutputFile abstract val report: RegularFileProperty @TaskAction fun run() = report.get().asFile.writeText("hello") } tasks.register<GenerateReport>("genReport") { report.set(layout.buildDirectory.file("reports/report.txt")) } ``` Here the output location is lazy and tracked: relocating `buildDirectory` automatically moves the report, and Gradle records it for incremental builds.
- What does layout.buildDirectory.file("x") return, and why isn't it just a File?A Provider<RegularFile>. Returning a Provider keeps resolution lazy so the path follows a relocated build dir and can be tracked as a task input/output.
- How do you get a plain java.io.File from a RegularFile?Call .getAsFile() (Kotlin: .asFile). For NIO use .asFile.toPath().
saying these in an interview costs you the question
- Saying project.buildDir is the modern way (it's deprecated in favor of layout.buildDirectory).
- Claiming layout.buildDirectory.file() returns a java.io.File.