Creating the correct elements in the Eclipse .classpath file

I am trying to use the Gradle Eclipse plug-in to build the .classpath file for an Eclipse project. By default, it looks like everything in the compile configuration is written to .classpath. For me, that is far too much, so I created configurations that contain the dependencies that I do and do not want in the .classpath file and then used those to construct the compile configuration, like this:

configurations {

needToBeInDotClasspath

mustNotBeInDotClasspath }

dependencies {

needToBeInDotClasspath

… // Add what’s needed (not shown)

mustNotBeInDotClasspath … // Add what’s needed (not shown)

compile configurations.needToBeInDotClasspath

compile configurations.mustNotBeInDotClasspath

}

My eclipse task is configured like this:

eclipse {

classpath {

minusConfigurations += configurations.compile

plusConfigurations += configurations.needToBeInDotClasspath

} }

This does not work – the entries from the configuration “needToBeInDotClasspath” are not written to the .classpath file. I am guessing this is because they appear in both “compile” and “needToBeInDotClasspath” and the minus is overriding the “plus”. Is there any way around this? I only want the class path entries from “needToBeInDotClasspath” to appear in the file, not anything extra that the Java plug-in might have added to the compile configuration.

The Java plugin doesn’t add anything to the ‘compile’ configuration; adding dependencies is completely left to the build script. Why would you want to have something on the Gradle compile class path, but not on the Eclipse class path? How is the code supposed to compile in Eclipse then?

In fact, the Eclipse plugin puts the whole ‘testRuntime’ configuration (which includes ‘compile’ and ‘runtime’) on the Eclipse class path. This seems to be a common practice, probably because it simplifies launching code (e.g. tests). It might also facilitate other activities like searching and debugging. (I’m not completely sure because I rarely use Eclipse myself.)

So maybe what you are after is only putting the Gradle ‘(test)Compile’ configuration on the Eclipse class path, but not the ‘(test)Runtime’ configuration. This should certainly be doable. The drawback is that you then also have to take care of generating Eclipse run configurations that provide the necessary runtime dependencies for different kinds of launches (tests, debugger, etc.). I don’t know how well that plays with run configurations implicitly generated by Eclipse, but I suppose you can generate the necessary templates.

Is the Gradle build a multi-project build with project dependencies? This would further complicate the task.

The compile classpath is not always going to be the same as the set of classpathentry elements in the .classpath file. In my case, I have several projects (B, C,…) that have build time dependencies on another project (A). The compile class path for B is the sum of the compile classpaths for A and B, but in the .classpath file for project B, I only want classpathentry elements for the items that project B adds.

More explicitly:

  • project A uses libX.jar and libY.jar. Its Gradle compile classpath will be libX.jar;libY.jar. Its .classpath file will include libX.jar and libY.ajr.

  • project B depends on project A and adds libZ.jar. Its Gradle compile classpth will be libX.jar;libY.jar;libZ.jar. It’s Eclipse .classpath file needs include a reference to project A (which I know how to create) and an entry for libZ.jar – but not libX.jar or libY.jar, because those are inherited from the project A reference.

In this example, the Eclipse plug-in will write classpathentry elements for all of libX.jar, libY.jar and libZ.jar, so I suppressed that by using minusConfigurations, as shown in my original question. I also set up a configuration (which I referred to as ‘needToBeInDotClasspath’), which in this case contains libZ.jar. I added this to the build script using plusConfigurations. The result is that none of the classpath entries make it to the .classpath file.

I know I can probably fix this by directly manipulating the XML, but is there a better way?

It’s Eclipse .classpath file needs include a reference to project A

The Eclipse plugin will generate the reference automatically.

and an entry for libZ.jar – but not libX.jar or libY.jar, because those are inherited from the project A reference.

If some code in B depends on libX and libY, it’s best to reflect this reality and add them directly to (Gradle and Eclipse) project B. Otherwise, they should be inherited from Eclipse project A, which needs to mark them as exported. This is exactly how the Eclipse plugin is supposed to behave, and it appears to work fine for me.

As to your original question, the problem is that methods like ‘Configuration.filter’ and ‘Configuration.minus’ return a ‘FileCollection’ rather than a ‘Configuration’. But ‘plus/minusConfigurations’ only accept 'Configuration’s, because the Eclipse plugin needs to be able to extract dependency information. The only solution I can think of is to extract the desired dependencies and stuff them into a new configuration. But first I’d reconsider if the vanilla Eclipse plugin isn’t good enough for your needs. Did you run ‘gradle eclipse’ from the Gradle root project? That’s how you generate linked Eclipse projects. With the Eclipse Gradle tooling, you’ll get even more control, and can partially import builds into Eclipse.

If some code in B depends on libX and libY, it’s best to reflect this reality and add them directly to (Gradle and Eclipse) project B.

I don’t think that’s the best way to handle this. Libraries that are exported by project A should not be listed in the .classpath file for project B.

Otherwise, they should be inherited from Eclipse project A, which needs to mark them as exported. This is exactly how the Eclipse plugin is supposed to behave, and it appears to work fine for me.

The Eclipse plugin’s .classpath file contains all of the Jar files. This isn’t necessary and it’s not how Eclipse does it if you create the projects manually.

The only solution I can think of is to extract the desired dependencies and stuff them into a new configuration.

That’s exactly what I did. There is a configuration that contains only the Jars for project B. I need the Eclipse plugin to write out classpathentry elements ONLY for the Jars in my configuration.

Did you run gradle eclipse from the Gradle root project? That’s how you generate linked Eclipse projects.

Yes, that’s what I am doing.

With the Eclipse Gradle tooling, you’ll get even more control, and can partially import builds into Eclipse. That doesn’t fit my workflow. This process needs to be automated at build time.

Using the compile classpath as a default for the content of the .classpath file is a good idea, but in this case it isn’t what I want, so I need a way to suppress this behavior. It looks like there isn’t a way, short directly manipulating the XML. If minusConfigurations and plusConfigurations were acted upon in the order in which they appear in the build script, this would work.

Libraries that are exported by project A should not be listed in the .classpath file for project B.

From all I can tell, they will only be listed if they are listed in Gradle project B, which is a very sensible behavior. The goal is not to end up with the fewest possible library declarations, but to express the semantic dependencies, in the same way that the Gradle build (hopefully) does. Of course, you can always adjust this on the Gradle side.

The Eclipse plugin’s .classpath file contains all of the Jar files.

Can you provide a reproducible example?

If minusConfigurations and plusConfigurations were acted upon in the order in which they appear in the build script, this would work.

The behavior follows a common include/exclude paradigm where “include” determines the base set, and “exclude” is subtracted from that.

From all I can tell, they will only be listed if they are listed in Gradle project B, which is a very sensible behavior. The goal is not to end up with the fewest possible library declarations, but to express the semantic dependencies, in the same way that the Gradle build (hopefully) does.

That’s one goal, but in this case it isn’t the goal I have in mind.

Can you provide a reproducible example? Unfortunately, I do not have a simple example.

The behavior follows a common include/exclude paradigm where “include” determines the base set, and “exclude” is subtracted from that.

That’s what I figured, so how about adding a big reset switch that removes everything and lets me start from scratch by using plusConfigurations?

That’s what I figured, so how about adding a big reset switch that removes everything and lets me start from scratch by using plusConfigurations?

You can just overwrite what’s there, by using ‘=’ instead of ‘+=’.

You can just override what’s there, by using = instead of +=.

You mean like this:

plusConfigurations = configurations.projectDependencies

This doesn’t work – I get this:

Cannot cast object ‘configuration ‘:charts:projectDependencies’’ with class ‘o rg.gradle.api.internal.artifacts.configurations.DefaultConfiguration_Decorated’ to class ‘java.util.Collection’

Also, I don’t think this is really what I need – I need to clear the underlying set of configurations that minusConfigurations/plusConfigurations is operating on, not the set of plusConfigurations.

The correct syntax is:

plusConfigurations = [configurations.projectDependencies]

Thanks for the correction. As I suspected, though, it doesn’t have the effect that I need. I guess I’m going to have to work at the XML level to fix this.