skip to content

What is a SourceSet in Gradle's Java plugin, and what two source sets does the plugin create by default?

level: juniorimportance: must knowfreq 70%

answer

  1. main + test created by java plugin
  2. named group of sources compiled together
  3. owns srcDirs, classpaths, configurations
  4. compileJava / processResources tasks
  5. test classpath includes main output

basics

~10 s

A SourceSet is a named group of source files compiled together. The java plugin creates two by default: main (production code) and test (test code that depends on main's output).

solid answer

~40 s

A `SourceSet` is a logical grouping of source files (Java + resources) that are compiled and processed as a unit. Applying the `java` plugin registers two source sets: `main` for production code and `test` for test code. Each source set owns its own source directories (`src/<name>/java`, `src/<name>/resources`), its own compile and runtime classpaths, and a generated `compileJava`/`processResources` task pair. The `main` source set produces the artifact that ends up in the JAR; `test` is configured so its compile and runtime classpaths include `main`'s output plus the test dependencies. Each source set also gets its own dependency configurations such as `implementation`, `testImplementation`, etc.

code

kotlin · 12 lines
kotlin
plugins {
    java
}

// Default layout the java plugin assumes:
// src/main/java, src/main/resources
// src/test/java, src/test/resources

dependencies {
    implementation("com.google.guava:guava:33.0.0-jre") // main source set
    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") // test source set
}

go deeper

for a junior

Name main and test, know the default src/<set>/java layout, and that the java plugin creates them.

for a middle

Explain the owned tasks, per-source-set configurations, and that test's classpath includes main's output.

for a senior

Discuss the SourceSet as the reusable abstraction enabling extra compilation units and how configurations derive from the set name.

for a principal

Frame source sets as the contract that keeps multi-unit builds consistent and explain governance implications for custom build conventions across many modules.

## What a SourceSet is A **SourceSet** is Gradle's model for a named collection of source files that are compiled together and share a classpath. It is the unit the `java` plugin uses to wire up compilation tasks, dependency configurations, and output directories. When you apply the `java` plugin (or anything that pulls it in, like `java-library` or `application`), Gradle creates a `sourceSets` container with two entries: - **`main`** — your production code. Defaults to `src/main/java` for sources and `src/main/resources` for resources. Its compiled classes go into `build/classes/java/main`. - **`test`** — your unit tests. Defaults to `src/test/java` and `src/test/resources`. Its classpath is pre-wired to include `main`'s compiled output. ## What each source set owns For a source set named `foo`, Gradle derives: - **Source directories**: a `java` `SourceDirectorySet` (`foo.java.srcDirs`) and a `resources` one (`foo.resources.srcDirs`). - **Tasks**: `compileFooJava` and `processFooResources`, plus `fooClasses` as an aggregating lifecycle task. - **Classpaths**: `foo.compileClasspath` and `foo.runtimeClasspath` — `FileCollection`s resolved from the matching configurations. - **Dependency configurations**: `fooImplementation`, `fooApi` (with `java-library`), `fooCompileOnly`, `fooRuntimeOnly`. For `main` the prefix is dropped, so you write `implementation` not `mainImplementation`. ## Why this matters The source-set abstraction is what lets Gradle support extra compilation units — integration tests, generated code, JMH benchmarks — without special-casing each one. Anything modeled as a source set automatically gets the full compile/classpath/configuration machinery. ```kotlin plugins { java } // The two defaults are already present: sourceSets { named("main") named("test") } ```

  • Where do the compiled classes of the `main` source set end up by default?
    In `build/classes/java/main` for `.class` files and `build/resources/main` for processed resources; these directories make up `main`'s output, which feeds the `jar` task and the `test` classpath.
  • Why doesn't the `main` source set's configuration prefix appear (e.g. `mainImplementation`)?
    By convention the `main` source set's configurations drop the name prefix, so they read as `implementation`, `api`, `compileOnly`, etc. Every other source set keeps the prefix.

Think of a source set like a separate 'room' in your project: each room has its own door (source dirs), its own furniture list (dependencies), and its own electrical circuit (classpath) — even though they all sit in the same house.

saying these in an interview costs you the question

  • Saying a source set is 'just a folder' — it's a model object that owns classpaths, tasks, and configurations, not merely a directory.
  • Claiming `test` automatically inherits `main`'s dependencies — it inherits `main`'s compiled output on the classpath, not `main`'s declared `implementation` dependencies unless they're on the runtime classpath.

context