How to declare dependencies in a Java multiproject setup with interdependent tests and Eclipse

buildship
eclipse

#1

I am trying to migrate a large Maven multiproject setup to Gradle (4.8.1). Many projects are Java but there are also C/C++ projects but I am not dealing with that yet - neither does/did Maven.
I am facing some challenges that seem to be caused by a combination of following characteristics:

  1. Not all projects are siblings in the same folder. There is a deeper structure to them - i.e. from the root project’s (root) directory there are zero, one or two folders before the subproject folders. These aren’t “real projects” in a sense that they themselves contain no source code, though - they are just grouping folders, but I include them as folders and configure their location in settings.dir using:
    include projectId;
    project(projectId).projectDir = dir;

  2. We want to benefit from strict dependency scoping and control. Any and all “implementation” dependencies we declare as both a non-transitive implementation dependency and as a transitive runtimeClasspath dependency. Same goes with testImplementation and testRuntimeClasspath. We do not add these to (test)runtimeOnly to honour the word “only” in their names.

  3. Projects depend on other projects in “simple” Java terms and we’re not using the java-library plugin - i.e. we’re not “exporting” any api dependencies by default. If a project A depends on project B that means:

  • A’s implementation depends on B’s implementation non-transitively (without transitive dependencies - any dependencies have to be explicitly declared)
  • A’s runtime depends on B’s runtime transitively (transitive dependencies included)
  • A’s testImplementation depends on B’s testImplementation non-transitively
  • A’s runtime depends on B’s testRuntime transitively
  1. We would like to automate Eclipse IDE and Eclipse workspace setup.

I was able to make the above work from command line (no Eclipse support) but I had to explicitly state that testImplementation configuration of dependent projects depend on either jars or test classes (build output) folders of dependency projects. But, when I do that, that messes up eclipseClasspath (used via the goomph plugin) a these end up being file dependencies not recognized as project ones.

To make Eclipse classpaths work I can just leave the “implementation project(…)” because Eclipse does not differentiate between scopes to the extent we need anyway (Photon does between main and test but not between compile and runtime).

If I try to not use goomph/eclipse plugins with eclipseClasspath task but Buildship (import Gradle projects) I face a different issue. If I don’t customize Eclipse project names then I get invalid project references in dependencies. If I add the following I resolve that issue (as also needed for goomph/eclipse - and it works there):

thatProject.eclipse {
  project {
    name = thatProject.path.substring(1).replace(':', '-');
  }

… I instead get an error during importing:

Synchronize Gradle projects with workspace failed due to an unexpected error.
java.lang.IllegalArgumentException: Path for project must have only one segment.
at org.eclipse.core.runtime.Assert.isLegal(Assert.java:63)
at org.eclipse.core.internal.resources.WorkspaceRoot.getProject(WorkspaceRoot.java:147)
at org.eclipse.core.internal.resources.Project.move(Project.java:961)
at org.eclipse.core.internal.resources.Project.move(Project.java:951)
at org.eclipse.buildship.core.workspace.internal.DefaultWorkspaceOperations.renameProject(DefaultWorkspaceOperations.java:354)
at org.eclipse.buildship.core.workspace.internal.ProjectNameUpdater.updateProjectName(ProjectNameUpdater.java:50)

How should this be approached correctly and cleanly?


#2

Here’s a diagram of generic concepts used with various well-structured Java other projects I’ve been working with (focused on Java here). I tried to mostly stay clear of Gradle terminology to allow more freedom of mapping of these to Gradle concepts and have only noted them, sort of, in the legend at the top left corner.


#3

I have one of my problems solved and am working around the other one.

Specifically, the exception seems to have been caused by the path of the root project. When I specifically gave an Eclipse project name for the root project that problem disappeared.

Otherwise I am now specifically configuring the in-gradle Eclipse plugin to only use a new dependency configuration I created specifically for Eclipse. All necessary dependencies get declared there as project dependencies as opposed to output classes dependencies for configurations used by gradle. Utility methods help with declaring this only once.

That does not go all the way as there is a lot to reconfigure. It would be nice if Gradle would offer a complete dependency configuration set as described in my previous message and realize that these are generic across domains.