Skip to content

Gradle multi-project import materializes internal build/libs jars into live JDT classpath #4399

@homecentral2022

Description

@homecentral2022

Summary

In a Gradle multi-project Java workspace, local subproject dependencies are resolved correctly as project references in the Buildship-saved model, but the live JDT/Eclipse project classpath is later materialized with internal build/libs/*.jar entries instead.

This causes local multi-module dependencies to behave like jar dependencies rather than live project/classfile dependencies, and can trigger stale or missing-library failures when those jars are rebuilt with different names.

Related discussion

I am filing here because the evidence points at the Java tooling / import / classpath materialization stack rather than a Cursor-specific layer.

Environment

  • OS: Windows 11
  • Java tooling: Language Support for Java by Red Hat
  • Build tool: Gradle multi-project build
  • Repo root: C:\Users\<USER>\git\<REPO>
  • Affected example project: admin-tools

Observed Java server startup includes:

  • -Djava.import.generatesMetadataFilesAtProjectRoot=false

Affected dependency shape

The affected project depends on local Gradle subprojects via normal project dependencies, for example:

  • project(":server-common")
  • project(":db-common")
  • project(":runtime-common")

Expected behavior

After import/reload, local Gradle subprojects should remain project references in the live Java project model.

The live classpath should not re-add internal jars from:

  • server-common/build/libs/*.jar
  • db-common/build/libs/*.jar
  • runtime-common/build/libs/*.jar

Actual behavior

After restart / re-import, the live classpath for admin-tools contains internal jars again, for example:

  • C:\Users\<USER>\git\<REPO>\runtime-common\build\libs\runtime-common-20260420175810.jar
  • C:\Users\<USER>\git\<REPO>\db-common\build\libs\db-common-20260420175810.jar
  • C:\Users\<USER>\git\<REPO>\server-common\build\libs\server-common-20260420175810.jar

These entries come back even if removed before restart.

Confirmed findings

1. Buildship-saved model is correct

File:

C:\Users\<USER>\AppData\Roaming\Cursor\User\workspaceStorage\<WORKSPACE_ID>\redhat.java\jdt_ws\.metadata\.plugins\org.eclipse.buildship.core\project-preferences\admin-tools

What was verified:

  • classpath= contains project refs such as /server-common, /db-common, /runtime-common
  • it contains normal external dependencies from .gradle\caches
  • it does not contain internal repo jar paths under <REPO>/*/build/libs

This strongly suggests the Buildship-side saved model is correct.

2. Live JDT/Eclipse classpath is wrong

File:

C:\Users\<USER>\AppData\Roaming\Cursor\User\workspaceStorage\<WORKSPACE_ID>\redhat.java\jdt_ws\.metadata\.plugins\org.eclipse.core.resources\.projects\admin-tools\.classpath

Observed contents include:

<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="lib" path="C:/Users/<USER>/git/<REPO>/runtime-common/build/libs/runtime-common-20260420175810.jar"/>
<classpathentry kind="lib" path="C:/Users/<USER>/git/<REPO>/db-common/build/libs/db-common-20260420175810.jar"/>
<classpathentry kind="lib" path="C:/Users/<USER>/git/<REPO>/server-common/build/libs/server-common-20260420175810.jar"/>

So the live Eclipse/JDT classpath is being materialized incorrectly after the Buildship model is already correct.

3. Repo-root .classpath is not the active source

Before restart, these were deleted:

  • repo-level admin-tools/.classpath
  • workspace org.eclipse.core.resources/.../admin-tools/.classpath
  • workspace org.eclipse.buildship.core/project-preferences/admin-tools

After restart:

  • the repo-level admin-tools/.classpath did not get recreated
  • the workspace live .classpath did get recreated
  • the Buildship project-preferences file also got recreated

This suggests the active regeneration is happening from the Java workspace/import pipeline, not from repo-root Eclipse metadata.

4. Gradle eclipse customization produced the correct model-side result

I tested targeted eclipse.classpath.file.whenMerged customization in affected subprojects to:

  • remove internal build/libs/*.jar entries
  • add explicit project refs such as /server-common, /db-common, /runtime-common

I then ran:

  • :admin-tools:eclipseClasspath
  • :db-common:eclipseClasspath
  • :runtime-common:eclipseClasspath

The generated .classpath outputs were correct immediately after generation, with project references present and internal local build/libs/*.jar entries absent.

This suggests the custom merge logic is not being ignored at the model-generation step.

5. Multiple importers were active in logs

Java language server logs showed multiple importers active, including:

  • GradleBuildServerProjectImporter
  • GradleProjectImporter
  • EclipseProjectImporter

That may be relevant if this is an importer precedence / post-processing issue.

6. Wrapper upgrade had no effect

I upgraded the project Gradle wrapper from 8.5 to 8.14.3 and re-tested.

The bad internal jar materialization still occurred, so this does not look like a simple mismatch between system Gradle and wrapper Gradle.

7. Disabling Gradle import was not a usable workaround

I also tested disabling java.import.gradle.enabled.

That severely broke import/build behavior rather than solving the classpath issue, so it does not appear to be a valid workaround.

Reproduction outline

  1. Open a Gradle multi-project Java workspace.
  2. Ensure one project depends on local subprojects via implementation project(...).
  3. Let Java/Gradle import complete.
  4. Inspect the Buildship-saved model under org.eclipse.buildship.core/project-preferences/....
  5. Inspect the live Eclipse/JDT project classpath under org.eclipse.core.resources/.projects/.../.classpath.
  6. Observe that the saved model contains project refs, but the live classpath contains internal build/libs/*.jar entries.

Impact

  • breaks expected live multi-module development workflow
  • makes the editor prefer built jar artifacts over local project/classfile references
  • can reintroduce stale or timestamped jar references on restart
  • can trigger missing-library failures when jar names change

Current hypothesis

The problem appears to happen after Buildship has already produced the correct model, during the step where org.eclipse.buildship.core.gradleclasspathcontainer is materialized into the live JDT/Eclipse project classpath.

If useful, I can provide the relevant anonymized project-preferences content, the resulting live .classpath, and the Java language server log excerpts showing the active importers.

Suggested investigation area

Please inspect how the Gradle/Buildship model is handed off to the live JDT project classpath, especially where org.eclipse.buildship.core.gradleclasspathcontainer is resolved and written into the live .classpath when local Gradle subprojects are already present as project references in the saved model.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions