(Multiple edits as can’t have more than three consecutive posts - see for resolution at the bottom, edit 5 … that isn’t a resolution - edit 6)
Turning off parallel builds in Eclipse (max builds set to 1) does not help.
Edit 1 (won’t let me create more posts):
Also another relevant link:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=550144
Edit 2: I just had an idea… will try later… Since these errors always appear on static resource files and (I think I remember that) there is a way to configure Eclipse to not copy them to the output folder (filter them) I could do that and try to add the source folders themselves to the (runtime) classpath.
Edit 3:
I was able to get exclusions to work in Eclipse manually. That addresses the problem. I was also able to configure Gradle Eclipse plugin to set them in the .classpath
files it generates:
eclipse {
classpath {
file {
whenMerged {
entries.each { entry ->
if (entry instanceof org.gradle.plugins.ide.eclipse.model.SourceFolder) {
entry.excludes += ['**']
}
}
}
}
}
}
(Above is verbose because I had some trouble filtering the collection otherwise)…
But none of this takes effect through Buildship. What to do?
Edit 4: https://github.com/eclipse/buildship/issues/271 is still open.
Edit 5 I believe I found a near-root cause, if not the root cause of this last night. Details follow…
A bit of a background as there are two issues working somewhat together here:
- Gradle offers us fine grain control over “dependency configurations” (and dependencies). These can depend on artifacts, local file system, other projects and (other) project configurations (and do some more). Some come out-of-box but we can create our own.
- One of the out-of-box configurations is (called) “
default
”. In Java world it brings together what the project’s code needs with that code itself, packaged in a jar. - Normally, when one project’s code expresses a “project” dependency it isn’t actually a project dependency at all but a dependency on that project’s
default
configuration. Difference is small but significant and important. If you think about it, if one project depends on another it means multiple similar but distinct things not captured by simplified dependency on project’sdefault
configuration. Imagine project A depending on project B. At high level this usually means the following:- A’s
implementation
depends on B’s code (without transitive dependencies in the strictest sense) - A’s runtime (
runtimeClasspath
) additionally depends on B’s transitive implementation dependencies + all runtime dependencies (transitively as well) - A’s
testImplementation
possibly depends on B’s test code (without transitive dependencies in the strictest sense) - A’s test runtime (
testRuntimeClasspath
) possibly additionally depends on B’s transitive test implementation dependencies + all test runtime dependencies (transitively as well)
- A’s
- Depending on
default
requires/depends on building jars which takes some time and space that isn’t always needed - say to run unit tests, as these can be run directly from unpackaged output directories.
Now:
- To get clean dependencies as in (3) and avoid dependency pollution that would stem from either explicit or implied dependencies on
default
configurations and to squeeze a little bit more build performance (4), we do not usedefault
configurations at all. We explicitly use clean configurations and define some of our own (to include actual output directories as opposed to jars, for example). - We do NOT separate Java source code and resources. In Gradle world we point both Java source and resources to the same directory. In our case separating them would be an anti-pattern:
- We do not need to process/transform our resources at all. Even if we did, that would be analogous to transforming
*.java
to*.class
. - Our resources must be directly related and “owned” by their Java classes. This way we can properly/cleanly encapsulate access to these resources.
- In some cases, the
*.java
files themselves are the static resources. - Due to the above the right location for these resources is together with the Java source code that owns them - everything else “breaks encapsulation” and makes those resources harder to see, find and work with.
- We do not need to process/transform our resources at all. Even if we did, that would be analogous to transforming
The issue:
- Buildship does not handle “Java source directory == static resources directory” setup well. This seems to be the main cause of the issue in this thread as it is possible that Eclipse and Buildship “fight” over processing resources (copying them to the output folder). During a build after cleaning nothing is expected in the output folder, yet the second processor will find the file already created by the first processor (probably Buildship).
- Inter-project dependencies end up missing in Eclipse (see Eclipse inter-project dependencies missing) probably because Buildship only looks at dependencies on
default
configuration that we specifically omit. This may also cause inappropriate build order and parallelism that exacerbates the main issue. In our case it also prevents proper inheritance of resources from one project to another. See Eclipse inter-project dependencies missing for a related thread.
Resolutions?
In the short term we will try to recognize/detect Buildship in/from our Gradle scripts and declare “default” default
configuration dependencies. We’ve confirmed that this addresses the inter-project dependencies issue. We are hoping that there is a way to detect Buildship indirectly, such as by setting some system property for Eclipse (as we don’t know of a direct way).
For Eclipse specifically we will also include actual source folders as runtime dependencies.
In the long term:
- Gradle should evolve beyond the insufficient way of declaring project dependencies to simply be dependencies on
default
configuration. - Gradle should handle “Java source directory == static resources directory” setup better.
- Buildship should not race and fight with Eclipse over resource creation.
- Buildship should also consider non-
default
configuration dependencies when determining Eclipse inter-project dependencies.
Edit 6: Actually the above isn’t a complete solution. I had a stretch of good luck it seems… but now it has run out. I have postponed the onset of the issue only. It no longer appears after first import in a fresh Eclipse but reappears after cleaning all projects (didn’t for a little while).