Buildship 2.0.1 breaks build with exported classpath entries

Since Buildship 2.0.1 (see Issue Remove user-defined lib entries from classpath · Issue #239 · eclipse/buildship · GitHub) classpath entries are removed from the .classpath file.
In our project we have a few legacy OSGI bundles that serve as “library projects” by exporting several JARs and their packages to make them usable in our other Eclipse RCP bundles. This was done by copying the dependencies to the root of the bundle and adding them as exported class path entries in the classpath file.

Up to Buildship 2.0.0 this was working fine but now all those entries in the .classpath file are removed by Buildship resulting in multiple compile errors and “Package … does not exist”.

After reading Adding a library to Eclipse classpath - #7 by mauromol
I tried solving this by setting the “exported” flag like this

eclipse.classpath.file.whenMerged { 
    def libs = entries.findAll { it.path.endsWith '.jar' }
    libs.each {library ->
	    library.exported = true
    }
}

This seems to work only partially since the “library projects” still have lots of “Package … does not exist in this plugin” messages.

Is there any way to make Buildship preserve these classpath entries? Can I safely “downgrade” from 2.0.1 to 2.0.0?

Kind regards,

Wolfgang

Can you please provide a small example project that shows the problem on GitHub? Handwritten dependencies should not be necessary, unless there is some problem in PDE that prevents it from referencing things in other classpath containers.

I have set up a small demo project on GitHub: https://github.com/loetifuss/buildship_2.0.1_issue.git

It consists of a library plugin exporting Groovy to a demo RCP bundle. The groovy lib needs to be pulled into the “libs” plugin by executing the task “retrieveJars” after importing it with Buildship to make it work in an RCP environment.
This setup works with Buildship 2.0.0 but with 2.0.1 the “libs” plugin will have “Package ‘groovy.*’ does not exist in this plugin” errors.

Note that if you comment out line 7 in “client-libraries.gradle” the module “rcp_bundle” will also start having Groovy compile errors.

Could you try exporting the whole Gradle classpath container? Maybe PDE can’t handle exported entries inside other containers.

This also seems to break the changes to the .classpath file that are made by the updateManifest task of the BundlePlugin (https://github.com/eclipse/buildship/blob/master/buildSrc/src/main/groovy/eclipsebuild/BundlePlugin.groovy). What is the proposed solution to this?

Regards, Thomas

Are you trying to work on Buildship? In that case please use our Oomph setup.

In case you copied this plugin for your own project: The lib dependencies should be defined as normal file dependencies in the build and any adjustments should be made through the eclipse.classpath DSL instead of the file mangling that we currently do.

Thanks for answering.

So the file.whenMerged hook it used to change the model of the classpath for a project? When I add something in the hook it is reflected in the Gradle / Buildship Classpath Container but not in the .classpath file.
So please correct me if I’m wrong here. The .classpath file is read and then augmented by the Buildship plugin. The result is visible under “Project and External Dependencies”.

So the problem I am facing now is that the bundle project (which has the Jar in /ib dir) compiles fine in the IDE but other projects that depend on this project cannot resolve their dependencies since the Jar file from the lib directory is not exported anymore. I could probably export the whole gradle classpath container but that would be too much of course.
So what could I do to further work with plugin-projects that wrap a Jar?

I’m not sure what you mean by that. The .classpath file is created by Buildship based on what is in the model.

No, Buildship creates a completely new .classpath file.

You need to export the Gradle container for PDE to pick up the contents. It seems PDE cannot handle classpath entries in containers well.

What I am observing is that I add a Library in the file.whenMerged hook and that library appears in the classpath container. The .classpath file is not changed.

So the modifications I make in the hook should appear there? Maybe the don’t appear because the change only has an influence on the classpath container. Is it still possible to add a classpath entry of type lib with the hook? I see a getKind() method in org.gradle.plugins.ide.eclipse.model.Library but no setter.

I’m looking at the examples at Buildship 1.0.18 is now available, especially the section on “Library, source and javadoc location”. Now if according to Remove user-defined lib entries from classpath · Issue #239 · eclipse/buildship · GitHub all user defined lib entries (what exactly is user defined?) are removed how what that sample make any sense anymore?

When I print out the entries passed to the whenMerged hook I have the following:

Output{path='bin'}
SourceFolder{path='src/main/java'
SourceFolder{path='src/main/resources'
Container{path='org.eclipse.jdt.launching.JRE_CONTAINER
Container{path='org.eclipse.pde.core.requiredPlugins'
Library{path='C:/Users/ThomasHofmann/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs
Library{path='C:/Users/ThomasHofmann/.gradle/caches/modules-2/files-2.1/org.eclipse.jdt
Library{path='lib/jaxb-impl-2.1.jar'

The c.lasspath on the other hand has only the following entries:

<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7/"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin"/>

So it is not exactly what is in the model. Is there some kind of documentation about how this works in detail?

So how would I do that since the gradle classpath container is not in entries when the whenMerged hook is called?

That’s the point of having a classpath container. The container is a placeholder that Buildship knows how to populate. It makes .classpath files portable.

Yes, of course. They are added to the container.

User-defined == Not defined in the build, but added manually in the IDE.

They are added to the container, where all lib/project entries belong.

You can add it using the eclipse.classpath.container method and then configure it as exported. Buildship just adds it by default if it wasn’t explicitly added to the model.

That’s a good hint. Thanks for the information so far. I will need to think about it and try some things.

I honestly haven’t worked much with the combination of PDE and Buildship. If you could experiment with the export setting (both on the container and on individual libs) and report your results, we could put together some reusable advice for others. The main problem seems to be that PDE doesn’t work well with other classpath containers.

Letting Buildship keep lib-entries outside of the container around is problematic because it can quickly lead to inconsistency between the command line and IDE classpath. That’s why we switched to actively removing them. However, if this turns out to be a blocker for PDE development together with Buildship, we might have to find a different compromise.

I am currently trying to migrate all my bundle projects that until now had custom lib entries in the .classpath file. I do this by using the whenMerged hook and adding a library. Now, I have come across a constelation that does not work. But let me explain what works first:

The bundle projects that include a JAR file in their lib subdirectory are changed so that a new Library is added to the entries in the hook. The fileReference method is passed a String like e.g.

lib/some.jar

This works as expected. The PDE tooling now somehow recognizes this as “part” of the bundle project and does not complain about the exported package names that come from the packages inside that JAR file and are written in the MANIFEST.MF file.

Now I got one “special” setup for one of my bundle projects. In that project I used to have custom library JARs added in the .classpath file. They look like this:

/someOtherProject/lib/subdir/someother.jar

The difference here is that the JAR file is located in some other project (that project is also used by Gradle using flatDir repository style). At build-time the JARs do not need to be included in the actual bundle because a custom class loading hook is active which loads the JARs from some pre-exisiting application on the file system. This explains the strange setup to some extend…

So, now when I add a library with fileReference(’/someOtherProject/lib/subdir/someother.jar’) I would have expected the same behaviour as I had with the custom entries in the .classpath file.
What actually happens is that the Gradle classpath container is using

C:\someOtherProject\lib\subdir\someother.jar

as path. I can see that because I have an error marker on the project. It says:

The container 'Project and External Dependencies' references non existing library 'C:\someOtherProject\lib\subdir\someother.jar

I know that this is probably a real strange setup but shouldn’t it be possible to add the same library entry (beginning with a / ) that I can make manually? I think eclipse will treat such entries relative to the workspace and not the filesystem.

This part is confusing and something we need to fix (cc @donat). When you pass a String to the fileReference method, that is treated as a project-relative variable. Instead, you can pass a File and it should work.

If I do new File(‘/someOtherProject/lib/subdir/someother.jar’) I get the same results.

You are creating an absolute path, that cannot work. Keep in mind we are talking about plain old java.io.File here, this has nothing to do with the Eclipse workspace model. You need to use file("path/of/lib/relative/to/project").