How do you customize the generated `.classpath` in Gradle — for example to mark a dependency as exported or tweak a classpath entry?
answer
- eclipse.classpath { containers / plusConfigurations }
- file.beforeMerged / whenMerged / withXml
- mutate typed entries (Library/SourceFolder)
- entry.isExported = true
- model not text — don't hand-edit
basics
~10 sUse the eclipse.classpath block. Add extra containers, configure plusConfigurations/minusConfigurations, and use the file.whenMerged hook to mutate the in-memory classpath model (e.g. set entry.exported = true) before it is written.
solid answer
~40 sThe `eclipse.classpath` DSL controls `.classpath` generation. You can declare `containers("…")` to add classpath containers (like a custom JRE), and adjust which configurations feed it via `plusConfigurations`/`minusConfigurations`. For fine-grained edits Gradle exposes lifecycle hooks on the in-memory model: `file.beforeMerged { classpath -> … }`, `file.whenMerged { classpath -> … }`, and `file.withXml { … }`. `whenMerged` runs after Gradle has built the entry list, so you iterate `classpath.entries`, find `Library`/`Container`/`SourceFolder` entries, and mutate them — a common case is marking a library `entry.isExported = true` so downstream Eclipse projects inherit it. `withXml` is the lowest level: you patch the raw DOM right before serialization. These hooks let you reconcile differences between Gradle's resolution and Eclipse's project model without hand-editing the file.
code
kotlin · 14 lineseclipse {
classpath {
containers("org.eclipse.jdt.launching.JRE_CONTAINER")
isDownloadSources = true
file {
whenMerged {
val cp = this as org.gradle.plugins.ide.eclipse.model.Classpath
cp.entries
.filterIsInstance<org.gradle.plugins.ide.eclipse.model.Library>()
.forEach { it.isExported = true }
}
}
}
}go deeper
Know the customization lives in eclipse.classpath { } rather than editing the file.
Name the three hooks (beforeMerged/whenMerged/withXml), their ordering, and a concrete mutation like setting isExported.
Explain the typed model vs. raw XML trade-off and how plus/minus configurations shape what gets emitted.
Standardize classpath customizations across many modules (convention plugin) to avoid per-project drift.
## The model behind `.classpath` Gradle does not template the `.classpath` file as text; it builds an **in-memory object model** (`org.gradle.plugins.ide.eclipse.model.Classpath`) of typed entries — `SourceFolder`, `Library`, `Container`, `Output`, `ProjectDependency` — then serializes it. The `eclipse.classpath` block lets you configure inputs and hook into that model at three stages. ## Configuring inputs ```kotlin eclipse { classpath { // Extra containers (e.g. a specific JRE container) containers("org.eclipse.jdt.launching.JRE_CONTAINER") // Add/remove configurations that contribute library entries plusConfigurations.add(configurations["integrationTestRuntimeClasspath"]) minusConfigurations.add(configurations["someProvidedConfig"]) // Download sources/javadoc jars for libraries isDownloadSources = true isDownloadJavadoc = false } } ``` ## The merge lifecycle hooks Each generated file exposes a `file` object with hooks: - **`beforeMerged { model -> … }`** — runs *before* Gradle merges its computed content onto any existing file. Use it to wipe or seed state so user edits don't accumulate. - **`whenMerged { model -> … }`** — runs *after* the merge, when the entry list is complete. This is where you mutate entries. - **`withXml { provider -> … }`** — operates on the raw XML DOM just before it is written. ### Marking a dependency exported ```kotlin eclipse { classpath { file { whenMerged { val cp = this as org.gradle.plugins.ide.eclipse.model.Classpath cp.entries .filterIsInstance<org.gradle.plugins.ide.eclipse.model.Library>() .forEach { it.isExported = true } } } } } ``` ## Why hooks instead of editing the file The file is **generated**, so manual edits are overwritten on the next `eclipseClasspath` run. The hooks keep the customization in the build script (reproducible, versioned) and apply on every regeneration. `beforeMerged`/`whenMerged` operate on the typed model (safer); `withXml` is the escape hatch for things the model doesn't expose.
- What is the difference between beforeMerged and whenMerged?`beforeMerged` runs before Gradle merges its content with any existing file (good for clearing stale state); `whenMerged` runs after, when the entry list is complete, so it's where you mutate or add entries.
- When would you use withXml instead of whenMerged?When you need to set raw XML attributes the typed model doesn't expose, or patch the serialized DOM directly. It's the lowest-level escape hatch.
- Why not just edit .classpath by hand?It is regenerated by `eclipseClasspath`, so manual edits are lost. Hooks keep the change in the build script and reproducible.
saying these in an interview costs you the question
- Editing the generated .classpath directly and expecting it to persist.
- Confusing beforeMerged and whenMerged ordering.
- Thinking withXml operates on the typed model — it operates on raw XML.