skip to content

How do you customize the generated `.classpath` in Gradle — for example to mark a dependency as exported or tweak a classpath entry?

level: middleimportance: should knowfreq 35%

answer

  1. eclipse.classpath { containers / plusConfigurations }
  2. file.beforeMerged / whenMerged / withXml
  3. mutate typed entries (Library/SourceFolder)
  4. entry.isExported = true
  5. model not text — don't hand-edit

basics

~10 s

Use 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 s

The `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 lines
kotlin
eclipse {
    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

for a junior

Know the customization lives in eclipse.classpath { } rather than editing the file.

for a middle

Name the three hooks (beforeMerged/whenMerged/withXml), their ordering, and a concrete mutation like setting isExported.

for a senior

Explain the typed model vs. raw XML trade-off and how plus/minus configurations shape what gets emitted.

for a principal

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.

context