The project was not built due to "Resource already exists on disk" errors in eclipse

I occasionally get errors in my generated eclipse projects that say The project was not built due to "Resource already exists on disk. (I should notw that I’m not a buildship user, I just generate projects and import them the old-fashioned way). The fix is to delete the bin directory, and refresh the project. The trouble is that sometimes it starts a cascade of errors, where I have to do this manually for every single project in my workspace.

It finally got annoying enough that I dug in, and the troubles start with Gradle 4.4, when it started creating separate output folders for different configurations.

It’s intermittent and hard to reproduce, but it’s happened in 3 different unrelated things I’m working on, so I finally built a plugin that makes gradle generate oldschool eclipse projects. Maybe the right answer is to adopt buildship or fix a bug in eclipse, but just figured I’d advertise it here in case anybody else bumps into the same problem: https://diffplug.github.io/goomph/javadoc/3.16.0/com/diffplug/gradle/eclipse/GradleClassicPlugin.html

2 Likes

I get the same thing now. Gradle 6.1.1 + Eclipse 2019-12 on Mac OS X. Few dozen projects, appears completely randomly (each time a different file) and only on resources, not *.class files. Cannot predict either the project or the file. Extremely annoying as, for me, it happens so reliably I can’t get rid of those, only hope they don’t occur on projects I am about to work on. Cleaning only “moves” the problem, even if “build automatically” is disabled and “build immediately” is not checked during “clean”.

Did not see this before (Gradle 5.6.3, Eclipse Photon or 2018-xx).

Also found these similar, possibly related reports:

https://diffplug.github.io/goomph/javadoc/3.17.4/com/diffplug/gradle/eclipse/GradleClassicPlugin.html
https://bugs.eclipse.org/bugs/show_bug.cgi?id=354226


https://betweengo.kimplicity.com/2012/07/02/fixing-resource-already-exists-on-disk-errors-eclipse/

If it can help… here’s one stack trace:

java.lang.Exception: Resource already exists on disk: '/someproject/bin/test/log4j2.properties'.
	at org.eclipse.core.internal.resources.ResourceException.provideStackTrace(ResourceException.java:42)
	at org.eclipse.core.internal.resources.ResourceException.<init>(ResourceException.java:38)
	at org.eclipse.core.internal.localstore.FileSystemResourceManager.copy(FileSystemResourceManager.java:329)
	at org.eclipse.core.internal.resources.Resource.copy(Resource.java:539)
	at org.eclipse.jdt.internal.core.builder.AbstractImageBuilder.copyResource(AbstractImageBuilder.java:401)
	at org.eclipse.jdt.internal.core.builder.BatchImageBuilder$3.visit(BatchImageBuilder.java:259)
	at org.eclipse.core.internal.resources.Resource.lambda$0(Resource.java:85)
	at org.eclipse.core.internal.watson.ElementTreeIterator.doIteration(ElementTreeIterator.java:85)
	at org.eclipse.core.internal.watson.ElementTreeIterator.doIteration(ElementTreeIterator.java:90)
	at org.eclipse.core.internal.watson.ElementTreeIterator.iterate(ElementTreeIterator.java:135)
	at org.eclipse.core.internal.resources.Resource.accept(Resource.java:94)
	at org.eclipse.core.internal.resources.Resource.accept(Resource.java:55)
	at org.eclipse.jdt.internal.core.builder.BatchImageBuilder.copyExtraResourcesBack(BatchImageBuilder.java:227)
	at org.eclipse.jdt.internal.core.builder.BatchImageBuilder.cleanOutputFolders(BatchImageBuilder.java:148)
	at org.eclipse.jdt.internal.core.builder.BatchImageBuilder.build(BatchImageBuilder.java:65)
	at org.eclipse.jdt.internal.core.builder.JavaBuilder.buildAll(JavaBuilder.java:278)
	at org.eclipse.jdt.internal.core.builder.JavaBuilder.build(JavaBuilder.java:192)
	at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:833)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:45)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:220)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:263)
	at org.eclipse.core.internal.events.BuildManager$1.run(BuildManager.java:316)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:45)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:319)
	at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:371)
	at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:392)
	at org.eclipse.core.internal.events.AutoBuildJob.doBuild(AutoBuildJob.java:154)
	at org.eclipse.core.internal.events.AutoBuildJob.run(AutoBuildJob.java:244)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:63)

(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:
:frowning_face: 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. :frowning_face:

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:

  1. 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.
  2. 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.
  3. 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’s default 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)
  4. 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 use default 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.

The issue:

  1. 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).
  2. 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: :exploding_head: 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).

Thanks a lot, this was so helpful.