It seems like the default behavior of the Eclipse plugin is to add runtime dependencies to Eclipse’s .classpath file.
Does this seem strange to anyone else? If it is a runtime dependency doesn’t that mean that you definitely shouldn’t need it listed in your .classpath file? Please correct me if I’m wrong. I have projects where the majority of libraries are runtime dependencies and I like to keep them out of the compile-time classpath.
For all I know, this is what Eclipse users typically do. The alternative is to have launch configurations for each project with the correct runtime dependencies (including transitive ones), which seems to be more difficult to handle.
This is not what Eclipse user should do. In Eclipse, runtime dependencies belong to launchers. The solution would be to import from a gradle build file specifying only compile dependencies, then add the runtime dependencies to the launchers, which can be done at import (by a specific user defined task, provided the launchers are in the project (also possible if they are in the workspace, but more difficult) and of course they exist at import time. The task would have to be run again if new launchers are created.
BTW, compile transitive dependencies also become direct, and all module dependencies are exported, which should also sound strange to any serious developer (although this is something most Eclipse user do)
How does this work in practice? Aren’t run configurations constantly created and destroyed, for example when “Run As…” is used? Then you have different types of run configurations, multiple projects with different class paths, changes in transitive dependencies, etc. I don’t see how this could work.
Run configurations (aka launchers) should not be constantly created and destroyed. If you are developing an application you will probably manually define one or several launchers. Selecting Run as… Simply create a default launcher, but there is little chances that it suits your needs. It only selects the project and main class, and set the working directory to the project diriectory. You would probably need to set the application parameters, the JVM parameters, the runtime classpath, the environment variables, and so on. As the launcher should probably be shared, you would also choose to save it in the project (rather than in the workspace) in order to add it to your VCS system.
Having the runtime dependencies added to the compile classpath only saves you the runtime dependencies configuration. On the other hand, having all dependencies (compile, test, runtime, either direct or transitive) mixed in the Eclipse compile classpath is a very bad thing because it creates dependencies that are not supposed to exist, destroying the modularity of the application.
How can this work in practice ? We use a build file in which runtime dependencies are included in conditionnal blocks. The condition tested is a parameter that must be set to true when build with gradle, and is implicitely false when importing into eclipse. We use the same principle for transitive dependencies, setting the transitive property to the value of the parameter. (One consieaunce is that we have to define transitive compile as direct, which is perfectly ok since I do not believe that “transitive compile” has any reasonable meaning.
We also use a custom task that open all launchers, removes the existing dependencies (if any) and adds the runtime dependencies. This is a simple task since a launcher is a simple XML file with a .launch extension.
The only trouble is that it is not dynamic, i.e. it has to be done manually each time dependencies are changed in the gradle build file. But this is also true for compile dependencies, so it is not a problem.
What is missing is the possibility to use parameters when importing into Eclipse. (I believe this is a limitation of the gradle api). The work around is to make the default configuration (no parameters) work for Eclipse and use parameters when the build is done on the CI server or locally through the gradle command line.
I’ve used Eclipse/STS for years. I’ve always had ALL dependencies in the project’s “Referenced Libraries”; I’ve never touched the classpath of a Configuration.
For each test class you “Run As… -> JUnit Test”, a Configuration is created. If the transitive/runtime dependencies were not part of your project, the test runs would fail, wouldn’t they?
Personally, I don’t have a problem with the way it currently is; it’s how I expect it to be. But, I could have been doing it wrong all this time.
I wouldn’t say you have been doing wrong all this time. I would say we have probably been doing very different things all this time. I am in a development team with lot of developers working on many shared projects. Running applications or tests in Eclipse are only a convenience to help developers to produce code that meet the requirement, i.e that compile and pass the tests on the CI server running Gradle builds. If the Eclipse build is not as close as possible to the CI build, it is useless, since what compiles locally could fail on the server.
BTW, if you don’t see any inconvenience in having all dependencies in the compile classpath, you should’nt be using Gradle ! On the opposite, if you think Gradle does a good job at managing several configurations for your dependencies, you should try to obtain the same result in Eclipse.
And no, Maven does not do this. Like Gradle, Maven uses configurations (called scopes), as do all build tools. Other IDEs do a better Job that Eclipse. For example, IntelliJ has separate test and compile scopes.
Eclipse has the worst system ever for managing dependencies. However, the m2eclipse plugin for MAven import does this, as well as the Ivy plugin. And this is complete nonsense!
Jamie is probably referring to ‘mvn eclipse:eclipse’, in which case he is right.
you should try to obtain the same result in Eclipse.
I agree, but unless Gradle controls the IDE build, it won’t ever be 100%. In the end, the Gradle build should be the judge. It’s not uncommon for developers to run a Gradle build before a commit, especially after bigger or more delicate changes. If a problem goes undetected every now and then, a fast CI loop will help to prevent further damage.
BTW, if you don’t see any inconvenience in having all dependencies in the compile classpath, you should’nt be using Gradle !
Putting all dependencies on the compile class path is simply a confession to the simplistic dependency model of Eclipse.
I don’t see how you could get the runtime class path set up automatically (per project!) for things like ‘Run as JUnit test’, which Eclipse developers use all the time. Also, you’d have to regenerate a launcher’s dependencies with every dependency change in a depended upon project. In comparison, putting everything on the compile class path seems like the lesser evil to me. Fortunately, Gradle is flexible enough to allow other solutions (like yours).
It’s not uncommon for developers to run a Gradle build before a commit, especially after bigger
or more delicate changes. If a problem goes undetected every now and then, a fast CI loop
will help to prevent further damage.
Testing the Gradle build in Eclipse before commiting is not possible at this time because the Gradle API is not comprehensive and does not allow using any parameters. We may of course test the build in a console. But a better solution is the Remote Run an Pre-tested Commit offered by our CI server (TeamCity).
I don’t see how you could get the runtime class path set up automatically (per project!) for
things like Run as JUnit test, which Eclipse developers use all the time.
We are considering putting the tests in separate Eclipse projects. This would have several other advantages, for example allowing using a different set of rules for quality checks.(We are using Checkstyle, and Eclipse does not allow different sets of rules based upon directory structure.)
Also, you’d have to regenerate a launcher’s dependencies with every dependency change
in a depended upon project.
This is not a problem, of course with Eclipse projects corresponding to Gradle modules of a same Gradle project. Regarding dependency to external projects, we do not allow dependencies on changing versions (snapshots). So, if a transitive dependency has changed, the depenedent project will have changed too (changing the depenency version) and the dependencies will have been refreshed (we would like this to be automatic!). So, the launchers will have been automatically refreshed at the same time. The only (minimal) problem is if a user creates a new launcher. He then has to refresh dependencies, as if he had had a new dependency or changed a dependency version.