How do you change or add source and resource directories for a source set, for example to support a non-standard project layout?
answer
- java / resources are SourceDirectorySet
- srcDir = append, srcDirs = replace
- register generated-sources dir
- wiring a task provider tracks dependency
- resources handled by processResources
basics
~10 sConfigure the source set's java.srcDirs (or resources.srcDirs) inside the sourceSets block. Use srcDir(...) to add a directory or assign srcDirs = listOf(...) to replace the defaults.
solid answer
~30 sEach source set exposes typed `SourceDirectorySet`s named `java` and `resources`. To add a directory you call `srcDir("path")`; to replace the convention entirely you assign `srcDirs = setOf("path")`. For example, to support a Maven-incompatible layout where Java lives under `src`, you write `sourceSets["main"].java.srcDirs("src")`. Adding rather than replacing is usually safer because it preserves the default `src/main/java`. Source directories are also where generated code is registered — pointing `java.srcDir` at a generated-sources folder makes that code part of compilation and incremental-build tracking. The `resources` set works identically for non-class files copied by `processResources`.
code
kotlin · 11 linesval genTask = tasks.register<MyCodeGen>("generateApi") {
outputDir.set(layout.buildDirectory.dir("generated/api"))
}
sourceSets {
named("main") {
// Adding a task's output: Gradle infers generateApi must run before compileJava
java.srcDir(genTask.flatMap { it.outputDir })
resources.srcDir("src/main/proto-resources")
}
}go deeper
Know that you can point a source set at a different directory via the sourceSets block.
Distinguish add vs. replace, and know java/resources are SourceDirectorySets.
Explain wiring generated output through providers for correct incremental builds and task dependency inference.
Standardize layout conventions across modules and reason about migration cost when changing source roots org-wide.
## SourceDirectorySet — the model behind directories Inside a source set, `java` and `resources` are `SourceDirectorySet` objects. A `SourceDirectorySet` is a `FileCollection` plus configuration for its root directories, include/exclude filters, and an output directory. You manipulate it through: - **`srcDir(Object)`** / **`srcDirs(Object...)`** — *append* one or more directories to the existing set. Paths are resolved relative to the project directory. - **`setSrcDirs(Iterable)`** / assigning `srcDirs = ...` — *replace* the whole set, discarding the convention default. - **`include(...)` / `exclude(...)`** — Ant-style filters on which files participate. ## Adding vs. replacing ```kotlin sourceSets { named("main") { // ADD a directory, keep src/main/java too java.srcDir("src/generated/java") // REPLACE the defaults entirely resources.setSrcDirs(listOf("config", "assets")) } } ``` Adding is the common case for **generated sources** (e.g. output of an annotation processor or a code-gen task): register the generation task's output directory via `java.srcDir(codegenTask)` so Gradle tracks the task dependency automatically and recompiles when generated code changes. Replacing is used when you adopt a **non-standard layout** — for instance a legacy project where all Java sits directly under `src/`. ## Why use the model rather than hardcoding paths Because `srcDir` accepts task providers and `Provider`s, wiring generated output through it gives you correct **incremental build** and **task dependency** inference for free — Gradle knows the codegen task must run before `compileJava`. Manually copying files would break up-to-date checking. ## Resources The `resources` `SourceDirectorySet` behaves the same way; its files are handled by `processResources`, which copies them into `build/resources/<set>` without compilation.
- Why is wiring a generation task's output via `srcDir(provider)` better than just adding the literal path?Passing the task provider lets Gradle infer the task dependency and track inputs/outputs, so `compileJava` automatically runs codegen first and stays up-to-date. A literal path string carries no dependency information.
- What's the difference between `srcDir` and assigning `srcDirs`?`srcDir(...)` appends to the existing directories (keeping defaults); assigning `srcDirs = ...` (or `setSrcDirs`) replaces them entirely, dropping the convention.
saying these in an interview costs you the question
- Replacing `srcDirs` when you only meant to add one, accidentally dropping `src/main/java`.
- Adding a generated-sources directory as a raw string and then wondering why codegen doesn't run before compilation — no task dependency was inferred.