skip to content

What does a source set's `output` represent, and how is it used to wire one source set against another?

level: seniorimportance: should knowfreq 35%

answer

  1. output = classesDirs + resourcesDir
  2. FileCollection carrying task dependencies
  3. add output, don't hardcode build/classes path
  4. classesDirs is a set (multi-language)
  5. output.dir registers extra outputs

basics

~10 s

A source set's output is a FileCollection of its compiled classes (classesDirs) and processed resources (resourcesDir). You add it to another set's classpath to let that set see these compiled artifacts.

solid answer

~40 s

`SourceSet.output` (a `SourceSetOutput`) is the set of directories produced by compiling and processing that source set: `output.classesDirs` (per-language compiled `.class` directories) and `output.resourcesDir` (processed resources). It is a `FileCollection` that carries built-in task dependencies on the compile and `processResources` tasks, so referencing it on another classpath also schedules the compilation. The canonical use is letting one source set depend on another's compiled output, e.g. `integrationTest.compileClasspath += sourceSets.main.get().output`. This is exactly how the `test` set is wired to `main` by default. Because `output` is a live `FileCollection` with task dependencies, you should add it rather than hardcoding `build/classes/...` paths — the latter would lose the implicit dependency and break ordering.

code

kotlin · 11 lines
kotlin
// Reuse main's compiled output on a custom set's classpath
val testFixtures by sourceSets.creating {
    compileClasspath += sourceSets.main.get().output
    runtimeClasspath += sourceSets.main.get().output
}

// Inspect output programmatically
tasks.register("printMainOutput") {
    val dirs = sourceSets.main.get().output.classesDirs
    doLast { dirs.forEach { println(it) } }
}

go deeper

for a junior

Know that a source set produces compiled classes under build/classes.

for a middle

Use sourceSets.main.output to share compiled classes with another set's classpath.

for a senior

Explain that output carries task dependencies and why hardcoded paths are wrong; know classesDirs is multi-language.

for a principal

Standardize cross-source-set wiring patterns and reason about how generated outputs registered via output.dir propagate through a multi-module graph.

## SourceSetOutput When you compile a source set Gradle writes: - compiled classes into `build/classes/<lang>/<set>` (e.g. `build/classes/java/main`) - processed resources into `build/resources/<set>` `SourceSet.getOutput()` returns a `SourceSetOutput`, a `FileCollection` exposing: - **`classesDirs`** — a `FileCollection` of the per-language class output dirs (a set, because a set can mix Java, Kotlin, Groovy each writing their own dir). - **`resourcesDir`** — the single processed-resources dir. - **`dirs`** — extra registered output dirs (e.g. via `output.dir(...)` for generated outputs). ## The key property: built-in task dependencies The crucial feature is that `output` is a **task-dependency-carrying** `FileCollection`. When you place `sourceSets.main.output` on another configuration or task input, Gradle automatically knows that `compileJava`/`processResources` for `main` must run first. You get correct ordering without an explicit `dependsOn`. ```kotlin val integrationTest by sourceSets.creating { // Adding output gives both the files AND the dependency on main's compile tasks compileClasspath += sourceSets.main.get().output runtimeClasspath += sourceSets.main.get().output } ``` ## Why not hardcode the path Writing `files("build/classes/java/main")` would point at the same files but **lose** the implicit task dependency and be brittle to layout changes. Always go through `output`. ## Registering extra outputs `output.dir("build/generated-output", builtBy = someTask)` registers an additional directory as part of the source set's output, useful when a task generates non-class artifacts that downstream sets must see. ## Relationship to `test` The default `test` wiring is literally `testCompileClasspath` and `testRuntimeClasspath` including `main.output` — the same mechanism you reuse for custom sets.

  • Why is `classesDirs` a collection rather than a single directory?
    A source set can host multiple JVM languages (Java, Kotlin, Groovy), and each compiler writes to its own output directory. `classesDirs` aggregates all of them so the full set of compiled classes is exposed.
  • What do you lose by putting `files("build/classes/java/main")` on a classpath instead of `sourceSets.main.output`?
    You lose the implicit task dependency on `compileJava`/`processResources`, so the dependent task may run before the classes exist, and you couple yourself to a hardcoded path that breaks if the layout changes.

saying these in an interview costs you the question

  • Hardcoding `build/classes/...` paths instead of using `output`, dropping the task dependency.
  • Assuming `classesDirs` is a single directory — it's a FileCollection to support multiple languages.

context